cvss-2.2.0/.cargo_vcs_info.json0000644000000001420000000000100120100ustar { "git": { "sha1": "134febe849609542db0d359e077237264221d7ab" }, "path_in_vcs": "cvss" }cvss-2.2.0/CHANGELOG.md000064400000000000000000000025031046102023000124140ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## 2.1.0 (2025-06-06) ### Added - CVSS 4.0 support ([#1285]) [#1285]: https://github.com/rustsec/rustsec/pull/1285 ## 2.0.0 (2022-05-01) ### Added - `no_std` support ([#549]) ### Changed - Structured `Error` type ([#546]) - Upgrade to 2021 edition; MSRV 1.56 ([#547]) - Flatten module structure ([#548]) ### Fixed - Computation for "no impact" vectors ([#399]) [#399]: https://github.com/RustSec/rustsec/pull/399 [#546]: https://github.com/RustSec/rustsec/pull/546 [#547]: https://github.com/RustSec/rustsec/pull/547 [#548]: https://github.com/RustSec/rustsec/pull/548 [#549]: https://github.com/RustSec/rustsec/pull/549 ## 1.0.2 (2021-05-10) ### Fixed - Dangling link in rustdoc ([#360]) [#360]: https://github.com/RustSec/rustsec/pull/360 ## 1.0.1 (2021-01-25) ### Changed - Rename default branch to `main` ## 1.0.0 (2019-09-23) - Migrate to GitHub Actions ## 0.3.0 (2019-09-06) - CVSS v3.0 support - severity: Add `FromStr` and `serde` support ## 0.2.0 (2019-08-28) - Add `Base::exploitability` and `impact` methods; docs - `serde` support ## 0.1.0 (2019-08-27) - Initial release cvss-2.2.0/Cargo.lock0000644000000034020000000000100077650ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "cvss" version = "2.2.0" dependencies = [ "serde", ] [[package]] name = "proc-macro2" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] [[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "2.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" cvss-2.2.0/Cargo.toml0000644000000027540000000000100100210ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2024" rust-version = "1.85" name = "cvss" version = "2.2.0" authors = ["Tony Arcieri "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Common Vulnerability Scoring System parser/serializer" readme = "README.md" keywords = [ "cvssv3", "cvssv4", "security", "advisory", "vulnerability", ] categories = ["parser-implementations"] license = "Apache-2.0 OR MIT" repository = "https://github.com/rustsec/rustsec" resolver = "2" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [features] default = [ "std", "v3", "v4", ] std = [] v3 = [] v4 = [] [lib] name = "cvss" path = "src/lib.rs" [[test]] name = "base" path = "tests/base.rs" [[test]] name = "redhat-v3" path = "tests/redhat-v3.rs" [[test]] name = "redhat-v4" path = "tests/redhat-v4.rs" [[test]] name = "uncovered-v4" path = "tests/uncovered-v4.rs" [dependencies.serde] version = "1" optional = true cvss-2.2.0/Cargo.toml.orig000064400000000000000000000012331046102023000134710ustar 00000000000000[package] name = "cvss" description = "Common Vulnerability Scoring System parser/serializer" version = "2.2.0" authors = ["Tony Arcieri "] license = "Apache-2.0 OR MIT" repository = "https://github.com/rustsec/rustsec" readme = "README.md" categories = ["parser-implementations"] keywords = ["cvssv3", "cvssv4", "security", "advisory", "vulnerability"] edition = "2024" rust-version = "1.85" [dependencies] serde = { workspace = true, optional = true } [features] default = ["std", "v3", "v4"] v3 = [] v4 = [] std = [] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] cvss-2.2.0/LICENSE-APACHE000064400000000000000000000251371046102023000125370ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. cvss-2.2.0/LICENSE-MIT000064400000000000000000000020641046102023000122410ustar 00000000000000Copyright (c) 2019-2021 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cvss-2.2.0/README.md000064400000000000000000000041031046102023000120600ustar 00000000000000# RustSec: Common Vulnerability Scoring System [![Latest Version][crate-image]][crate-link] [![Docs][docs-image]][docs-link] [![Build Status][build-image]][build-link] [![Safety Dance][safety-image]][safety-link] ![MSRV][rustc-image] ![Apache 2.0 OR MIT licensed][license-image] [![Project Chat][zulip-image]][zulip-link] Rust implementation of the [Common Vulnerability Scoring System (Version 3.1 and 4.0) Specification][spec]. [Documentation][docs-link] ## Minimum Supported Rust Version Rust **1.60** or higher. Minimum supported Rust version can be changed in the future, but it will be done with a minor version bump. ## License Licensed under either of: - Apache License, Version 2.0 ([LICENSE-APACHE] or ) - MIT license ([LICENSE-MIT] or ) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions. [//]: # (badges) [crate-image]: https://img.shields.io/crates/v/cvss.svg?logo=rust [crate-link]: https://crates.io/crates/cvss [docs-image]: https://docs.rs/cvss/badge.svg [docs-link]: https://docs.rs/cvss/ [build-image]: https://github.com/RustSec/rustsec/actions/workflows/cvss.yml/badge.svg [build-link]: https://github.com/RustSec/rustsec/actions/workflows/cvss.yml [safety-image]: https://img.shields.io/badge/unsafe-forbidden-success.svg [safety-link]: https://github.com/rust-secure-code/safety-dance/ [rustc-image]: https://img.shields.io/badge/rustc-1.60+-blue.svg [license-image]: https://img.shields.io/badge/license-Apache2.0%2FMIT-blue.svg [zulip-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [zulip-link]: https://rust-lang.zulipchat.com/#narrow/stream/146229-wg-secure-code/ [//]: # (general links) [spec]: https://www.first.org/cvss/specification-document [LICENSE-APACHE]: https://github.com/RustSec/cargo-audit/blob/main/LICENSE-APACHE [LICENSE-MIT]: https://github.com/RustSec/cargo-audit/blob/main/LICENSE-MIT cvss-2.2.0/src/cvss.rs000064400000000000000000000152241046102023000127220ustar 00000000000000use alloc::boxed::Box; use alloc::str::FromStr; use core::fmt; #[cfg(feature = "v3")] use crate::v3; #[cfg(feature = "v4")] use crate::v4; use crate::{ Severity, error::{Error, Result}, }; use alloc::borrow::ToOwned; #[cfg(feature = "serde")] use { alloc::string::String, alloc::string::ToString, serde::{Deserialize, Serialize, de, ser}, }; /// Prefix used by all CVSS strings pub const PREFIX: &str = "CVSS"; /// A CVSS vector #[derive(Clone, PartialEq, Eq, Debug)] #[non_exhaustive] pub enum Cvss { #[cfg(feature = "v3")] /// A CVSS 3.0 base vector CvssV30(v3::Base), #[cfg(feature = "v3")] /// A CVSS 3.1 base vector CvssV31(v3::Base), #[cfg(feature = "v4")] /// A CVSS 4.0 vector CvssV40(v4::Vector), } impl Cvss { /// Get the score of this CVSS vector /// /// The different versions of CVSS have dedicated `Score` types. /// For CVSSv4 specifically, the dedicated type includes the nomenclature information. #[cfg(feature = "std")] pub fn score(&self) -> f64 { match self { #[cfg(feature = "v3")] Self::CvssV30(base) => base.score().value(), #[cfg(feature = "v3")] Self::CvssV31(base) => base.score().value(), #[cfg(feature = "v4")] Self::CvssV40(vector) => vector.score().value(), } } /// Get the severity of this CVSS vector #[cfg(feature = "std")] pub fn severity(&self) -> Severity { match self { #[cfg(feature = "v3")] Self::CvssV30(base) => base.score().severity(), #[cfg(feature = "v3")] Self::CvssV31(base) => base.score().severity(), #[cfg(feature = "v4")] Self::CvssV40(vector) => vector.score().severity(), } } /// Get an iterator over all defined metrics pub fn metrics(&self) -> Box + '_> { match self { #[cfg(feature = "v3")] Self::CvssV30(base) => Box::new(base.metrics().map(|(m, v)| (MetricType::V3(m), v))), #[cfg(feature = "v3")] Self::CvssV31(base) => Box::new(base.metrics().map(|(m, v)| (MetricType::V3(m), v))), #[cfg(feature = "v4")] Self::CvssV40(vector) => { Box::new(vector.metrics().map(|(m, v)| (MetricType::V4(m), v))) } } } } #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl<'de> Deserialize<'de> for Cvss { fn deserialize>( deserializer: D, ) -> core::result::Result { String::deserialize(deserializer)? .parse() .map_err(de::Error::custom) } } #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl Serialize for Cvss { fn serialize( &self, serializer: S, ) -> core::result::Result { self.to_string().serialize(serializer) } } impl FromStr for Cvss { type Err = Error; fn from_str(s: &str) -> Result { // Parse the prefix and select the right vector parser let (id, _) = s.split_once('/').ok_or(Error::InvalidComponent { component: s.to_owned(), })?; let (prefix, version) = id.split_once(':').ok_or(Error::InvalidComponent { component: id.to_owned(), })?; let (major_version, minor_version) = version.split_once('.').ok_or(Error::InvalidComponent { component: id.to_owned(), })?; match (prefix, major_version, minor_version) { #[cfg(feature = "v3")] (PREFIX, "3", "0") => v3::Base::from_str(s).map(Self::CvssV30), #[cfg(feature = "v3")] (PREFIX, "3", "1") => v3::Base::from_str(s).map(Self::CvssV31), #[cfg(feature = "v4")] (PREFIX, "4", "0") => v4::Vector::from_str(s).map(Self::CvssV40), (PREFIX, _, _) => Err(Error::UnsupportedVersion { version: version.to_owned(), }), (_, _, _) => Err(Error::InvalidPrefix { prefix: prefix.to_owned(), }), } } } impl fmt::Display for Cvss { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { #[cfg(feature = "v3")] Self::CvssV30(base) => write!(f, "{}", base), #[cfg(feature = "v3")] Self::CvssV31(base) => write!(f, "{}", base), #[cfg(feature = "v4")] Self::CvssV40(vector) => write!(f, "{}", vector), } } } /// Metric type (across CVSS versions) #[non_exhaustive] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum MetricType { V3(v3::MetricType), V4(v4::MetricType), } impl MetricType { /// Get the name of this metric (i.e. acronym) pub fn name(self) -> &'static str { match self { Self::V3(m) => m.name(), Self::V4(m) => m.name(), } } /// Get a description of this metric. pub fn description(self) -> &'static str { match self { Self::V3(m) => m.description(), Self::V4(m) => m.description(), } } } #[cfg(all(feature = "std", test))] mod tests { use super::{Cvss, Error}; use alloc::borrow::ToOwned; #[test] #[cfg(feature = "v3")] fn test_parse_v3() { let vector = "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N".parse::(); assert!(vector.is_ok()); let vector = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N".parse::(); assert!(vector.is_ok()); } #[test] #[cfg(feature = "v4")] fn test_parse_v4() { let vector = "CVSS:4.0/AV:P/AC:H/AT:P/PR:L/UI:P/VC:H/VI:H/VA:H/SC:L/SI:L/SA:L/E:A/S:P/AU:Y/R:A/V:D/RE:L/U:Red".parse::(); assert!(vector.is_ok()); } #[test] fn test_parse_invalid() { let err = "CVSS:5.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N" .parse::() .unwrap_err(); assert_eq!( err, Error::UnsupportedVersion { version: "5.0".to_owned() } ); let err = "CSS:4.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N" .parse::() .unwrap_err(); assert_eq!( err, Error::InvalidPrefix { prefix: "CSS".to_owned() } ); let err = "garbage/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N" .parse::() .unwrap_err(); assert_eq!( err, Error::InvalidComponent { component: "garbage".to_owned() } ); } } cvss-2.2.0/src/error.rs000064400000000000000000000104061046102023000130720ustar 00000000000000//! Error types use crate::v3; #[cfg(feature = "v4")] use crate::v4; use alloc::string::String; use core::fmt; /// Result type with the `cvss` crate's [`Error`] type. pub type Result = core::result::Result; /// Kinds of errors #[derive(Clone, Debug, Eq, PartialEq)] #[non_exhaustive] pub enum Error { /// Invalid component of a CVSS metric group. InvalidComponent { /// Invalid component. component: String, }, /// Invalid metric for CVSSv3. InvalidMetric { /// The metric that was invalid. metric_type: v3::metric::MetricType, /// The value that was provided which is invalid. value: String, }, #[cfg(feature = "v4")] /// Invalid metric for CVSSv4. InvalidMetricV4 { /// The metric that was invalid. metric_type: v4::MetricType, /// The value that was provided which is invalid. value: String, }, #[cfg(feature = "v4")] /// Missing metric for CVSSv4. MissingMandatoryMetricV4 { /// Prefix which is missing. metric_type: v4::MetricType, }, #[cfg(feature = "v4")] /// Metric is duplicated for CVSSv4. DuplicateMetricV4 { /// Prefix which is doubled. metric_type: v4::MetricType, }, #[cfg(feature = "v4")] /// Invalid nomenclature for CVSSv4. InvalidNomenclatureV4 { /// Unknown CBSSv4 nomenclature. nomenclature: String, }, /// Invalid CVSS string prefix. InvalidPrefix { /// Prefix which is invalid. prefix: String, }, /// Invalid severity InvalidSeverity { /// Provided name which was unrecognized. name: String, }, /// Unknown metric name. UnknownMetric { /// Provided name which was unrecognized. name: String, }, /// Unsupported CVSS version UnsupportedVersion { /// Provided version string. version: String, }, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::InvalidComponent { component } => { write!(f, "invalid CVSS metric group component: `{component}`") } Error::InvalidMetric { metric_type, value } => { write!( f, "invalid CVSSv4 {} ({}) metric: `{}`", metric_type.name(), metric_type.description(), value ) } #[cfg(feature = "v4")] Error::InvalidMetricV4 { metric_type, value } => { write!( f, "invalid CVSSv4 {} ({}) metric: `{}`", metric_type.name(), metric_type.description(), value ) } #[cfg(feature = "v4")] Error::DuplicateMetricV4 { metric_type } => { write!( f, "duplicate CVSSv4 {} ({}) metric", metric_type.name(), metric_type.description(), ) } #[cfg(feature = "v4")] Error::MissingMandatoryMetricV4 { metric_type } => { write!( f, "missing mandatory CVSSv4 {} ({}) metric", metric_type.name(), metric_type.description(), ) } #[cfg(feature = "v4")] Error::InvalidNomenclatureV4 { nomenclature } => { write!(f, "invalid CVSSv4 nomenclature: `{}`", nomenclature) } Error::InvalidPrefix { prefix } => { write!(f, "invalid CVSS string prefix: `{prefix}`") } Error::InvalidSeverity { name } => { write!(f, "invalid CVSS Qualitative Severity Rating: `{name}`") } Error::UnknownMetric { name } => write!(f, "unknown CVSS metric name: `{name}`"), Error::UnsupportedVersion { version } => { write!(f, "unsupported CVSS version: {version}") } } } } #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for Error {} cvss-2.2.0/src/lib.rs000064400000000000000000000032131046102023000125050ustar 00000000000000#![no_std] #![cfg_attr(docsrs, feature(doc_cfg))] #![doc = include_str!("../README.md")] #![doc(html_logo_url = "https://raw.githubusercontent.com/RustSec/logos/main/rustsec-logo-lg.png")] #![forbid(unsafe_code)] #![warn(missing_docs, rust_2018_idioms, unused_qualifications)] //! ## Usage //! //! The [`Cvss`] type provides a unified interface for working with CVSS //! vectors. //! //! The [`v3::Base`] type provides the main functionality currently implemented //! for CVSS v3, namely: support for parsing, serializing, and scoring //! `CVSS:3.0` and `CVSS:3.1` Base Metric Group vector strings as described in //! the [CVSS v3.1 Specification]. //! //! The [`v4::Vector`] type provides a fully-featured implementation of CVSS //! v4.0, as described in the [CVSS v4.0 Specification]. //! //! Serde support is available through the optional `serde` Cargo feature. //! //! [CVSS v3.1 Specification]: https://www.first.org/cvss/v3.1/specification-document //! [CVSS v4.0 Specification]: https://www.first.org/cvss/v4.0/specification-document // TODO(tarcieri): CVSS v2.0, CVSS v3.1 Temporal and Environmental Groups extern crate alloc; #[cfg(feature = "std")] extern crate std; // A part of the v3 API is exposed even without the feature for compatibility. pub mod v3; #[cfg(feature = "v4")] pub mod v4; #[cfg(any(feature = "v3", feature = "v4"))] mod cvss; mod error; mod severity; // For compatibility pub use crate::v3::metric::{Metric, MetricType}; #[cfg(any(feature = "v3", feature = "v4"))] pub use crate::cvss::Cvss; pub use crate::{ error::{Error, Result}, severity::Severity, }; /// Prefix used by all CVSS strings pub const PREFIX: &str = "CVSS"; cvss-2.2.0/src/severity.rs000064400000000000000000000050661046102023000136210ustar 00000000000000//! Qualitative Severity Rating Scale use crate::{Error, Result}; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; #[cfg(feature = "serde")] use { alloc::string::String, serde::{Deserialize, Serialize, de, ser}, }; /// Qualitative Severity Rating Scale /// /// Described in CVSS v3.1 Specification: Section 5: /// /// /// And in CVSS v4.0 Specification: Section 6: /// /// /// The rating scales in v3 and v4 are the same. /// /// > For some purposes it is useful to have a textual representation of the /// > scores. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] pub enum Severity { /// None: CVSS Score 0.0 None, /// Low: CVSS Score 0.1 - 3.9 Low, /// Medium: CVSS Score 4.0 - 6.9 Medium, /// High: CVSS Score 7.0 - 8.9 High, /// Critical: CVSS Score 9.0 - 10.0 Critical, } impl Severity { /// Get a `str` describing the severity level pub fn as_str(self) -> &'static str { match self { Severity::None => "none", Severity::Low => "low", Severity::Medium => "medium", Severity::High => "high", Severity::Critical => "critical", } } } impl FromStr for Severity { type Err = Error; fn from_str(s: &str) -> Result { match s.to_ascii_lowercase().as_str() { "none" => Ok(Severity::None), "low" => Ok(Severity::Low), "medium" => Ok(Severity::Medium), "high" => Ok(Severity::High), "critical" => Ok(Severity::Critical), _ => Err(Error::InvalidSeverity { name: s.to_owned() }), } } } impl fmt::Display for Severity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.as_str()) } } #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl<'de> Deserialize<'de> for Severity { fn deserialize>( deserializer: D, ) -> core::result::Result { String::deserialize(deserializer)? .parse() .map_err(de::Error::custom) } } #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl Serialize for Severity { fn serialize( &self, serializer: S, ) -> core::result::Result { self.as_str().serialize(serializer) } } cvss-2.2.0/src/v3/base/a.rs000064400000000000000000000073421046102023000134300ustar 00000000000000//! Availability Impact (A) use crate::{Error, Metric, MetricType, Result}; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Availability Impact (A) - CVSS v3.1 Base Metric Group /// /// Described in CVSS v3.1 Specification: Section 2.3.3: /// /// /// > This metric measures the impact to the availability of the impacted /// > component resulting from a successfully exploited vulnerability. /// > While the Confidentiality and Integrity impact metrics apply to the /// > loss of confidentiality or integrity of data (e.g., information, /// > files) used by the impacted component, this metric refers to the loss /// > of availability of the impacted component itself, such as a networked /// > service (e.g., web, database, email). Since availability refers to the /// > accessibility of information resources, attacks that consume network /// > bandwidth, processor cycles, or disk space all impact the availability /// > of an impacted component. The Base Score is greatest when the /// > consequence to the impacted component is highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Availability { /// None (N) /// /// > There is no impact to availability within the impacted component. None, /// Low (L) /// /// > Performance is reduced or there are interruptions in resource /// > availability. Even if repeated exploitation of the vulnerability /// > is possible, the attacker does not have the ability to completely /// > deny service to legitimate users. The resources in the impacted /// > component are either partially available all of the time, or fully /// > available only some of the time, but overall there is no direct, /// > serious consequence to the impacted component. Low, /// High (H) /// /// > There is a total loss of availability, resulting in the attacker /// > being able to fully deny access to resources in the impacted /// > component; this loss is either sustained (while the attacker /// > continues to deliver the attack) or persistent (the condition /// > persists even after the attack has completed). Alternatively, /// > the attacker has the ability to deny some availability, but /// > the loss of availability presents a direct, serious consequence /// > to the impacted component (e.g., the attacker cannot disrupt /// > existing connections, but can prevent new connections; the /// > attacker can repeatedly exploit a vulnerability that, in each /// > instance of a successful attack, leaks a only small amount of /// > memory, but after repeated exploitation causes a service to become /// > completely unavailable). High, } impl Metric for Availability { const TYPE: MetricType = MetricType::A; fn score(self) -> f64 { match self { Availability::None => 0.0, Availability::Low => 0.22, Availability::High => 0.56, } } fn as_str(self) -> &'static str { match self { Availability::None => "N", Availability::Low => "L", Availability::High => "H", } } } impl fmt::Display for Availability { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for Availability { type Err = Error; fn from_str(s: &str) -> Result { match s { "N" => Ok(Availability::None), "L" => Ok(Availability::Low), "H" => Ok(Availability::High), _ => Err(Error::InvalidMetric { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v3/base/ac.rs000064400000000000000000000072001046102023000135640ustar 00000000000000//! Attack Complexity (AC) use crate::{Error, Metric, MetricType, Result}; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Attack Complexity (AC) - CVSS v3.1 Base Metric Group /// /// Described in CVSS v3.1 Specification: Section 2.1.2: /// /// /// > This metric describes the conditions beyond the attacker’s control that /// > must exist in order to exploit the vulnerability. As described below, /// > such conditions may require the collection of more information about the /// > target, or computational exceptions. Importantly, the assessment of this /// > metric excludes any requirements for user interaction in order to exploit /// > the vulnerability (such conditions are captured in the User Interaction /// > metric). If a specific configuration is required for an attack to /// > succeed, the Base metrics should be scored assuming the vulnerable /// > component is in that configuration. The Base Score is greatest for the /// > least complex attacks. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum AttackComplexity { /// High (H) /// /// > A successful attack depends on conditions beyond the attacker's control. /// > That is, a successful attack cannot be accomplished at will, but requires /// > the attacker to invest in some measurable amount of effort in preparation /// > or execution against the vulnerable component before a successful attack /// > can be expected. For example, a successful attack may depend on an /// > attacker overcoming any of the following conditions: /// > /// > - The attacker must gather knowledge about the environment in which /// > the vulnerable target/component exists. For example, a requirement /// > to collect details on target configuration settings, sequence numbers, /// > or shared secrets. /// > - The attacker must prepare the target environment to improve exploit /// > reliability. For example, repeated exploitation to win a race /// > condition, or overcoming advanced exploit mitigation techniques. /// > - The attacker must inject themselves into the logical network path /// > between the target and the resource requested by the victim in order /// > to read and/or modify network communications (e.g., a man in the middle /// > attack). High, /// Low (L) /// /// > Specialized access conditions or extenuating circumstances do not exist. /// > An attacker can expect repeatable success when attacking the vulnerable component. Low, } #[allow(clippy::derivable_impls)] impl Default for AttackComplexity { fn default() -> AttackComplexity { AttackComplexity::High } } impl Metric for AttackComplexity { const TYPE: MetricType = MetricType::AC; fn score(self) -> f64 { match self { AttackComplexity::High => 0.44, AttackComplexity::Low => 0.77, } } fn as_str(self) -> &'static str { match self { AttackComplexity::High => "H", AttackComplexity::Low => "L", } } } impl fmt::Display for AttackComplexity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for AttackComplexity { type Err = Error; fn from_str(s: &str) -> Result { match s { "H" => Ok(AttackComplexity::High), "L" => Ok(AttackComplexity::Low), _ => Err(Error::InvalidMetric { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v3/base/av.rs000064400000000000000000000112221046102023000136060ustar 00000000000000//! CVSS v3.1 Base Metric Group - Attack Vector (AV) use crate::{Error, Metric, MetricType}; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Attack Vector (AV) - CVSS v3.1 Base Metric Group /// /// Described in CVSS v3.1 Specification: Section 2.1.1: /// /// /// > This metric reflects the context by which vulnerability exploitation /// > is possible. This metric value (and consequently the Base Score) will be /// > larger the more remote (logically, and physically) an attacker can be in /// > order to exploit the vulnerable component. The assumption is that the /// > number of potential attackers for a vulnerability that could be exploited /// > from across a network is larger than the number of potential attackers /// > that could exploit a vulnerability requiring physical access to a device, /// > and therefore warrants a greater Base Score. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum AttackVector { /// Physical (P) /// /// > The attack requires the attacker to physically touch or manipulate /// > the vulnerable component. Physical interaction may be brief /// > (e.g., evil maid attack) or persistent. An example of such an attack /// > is a cold boot attack in which an attacker gains access to disk /// > encryption keys after physically accessing the target system. /// > Other examples include peripheral attacks via FireWire/USB Direct /// > Memory Access (DMA). Physical, /// Local (L) /// /// > The vulnerable component is not bound to the network stack and the /// > attacker’s path is via read/write/execute capabilities. Either: /// > /// > - the attacker exploits the vulnerability by accessing the target /// > system locally (e.g., keyboard, console), or remotely (e.g., SSH) /// > - the attacker relies on User Interaction by another person to /// > perform actions required to exploit the vulnerability (e.g., using /// > social engineering techniques to trick a legitimate user into /// > opening a malicious document). Local, /// Adjacent (A) /// /// > The vulnerable component is bound to the network stack, but the /// > attack is limited at the protocol level to a logically adjacent /// > topology. This can mean an attack must be launched from the same /// > shared physical (e.g., Bluetooth or IEEE 802.11) or logical /// > (e.g., local IP subnet) network, or from within a secure or /// > otherwise limited administrative domain (e.g., MPLS, secure VPN to /// > an administrative network zone). One example of an Adjacent attack /// > would be an ARP (IPv4) or neighbor discovery (IPv6) flood leading to /// > a denial of service on the local LAN segment (e.g., CVE‑2013‑6014). Adjacent, /// Network (N) /// /// > The vulnerable component is bound to the network stack and the set of /// > possible attackers extends beyond the other options listed below, up to /// > and including the entire Internet. Such a vulnerability is often /// > termed “remotely exploitable” and can be thought of as an attack being /// > exploitable at the protocol level one or more network hops away /// > (e.g., across one or more routers). An example of a network attack is /// > an attacker causing a denial of service (DoS) by sending a specially /// > crafted TCP packet across a wide area network (e.g., CVE‑2004‑0230). Network, } impl Metric for AttackVector { const TYPE: MetricType = MetricType::AV; fn score(self) -> f64 { match self { AttackVector::Physical => 0.20, AttackVector::Local => 0.55, AttackVector::Adjacent => 0.62, AttackVector::Network => 0.85, } } fn as_str(self) -> &'static str { match self { AttackVector::Physical => "P", AttackVector::Local => "L", AttackVector::Adjacent => "A", AttackVector::Network => "N", } } } impl fmt::Display for AttackVector { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for AttackVector { type Err = Error; fn from_str(s: &str) -> Result { match s { "P" => Ok(AttackVector::Physical), "L" => Ok(AttackVector::Local), "A" => Ok(AttackVector::Adjacent), "N" => Ok(AttackVector::Network), _ => Err(Error::InvalidMetric { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v3/base/c.rs000064400000000000000000000054751046102023000134370ustar 00000000000000//! Confidentiality Impact (C) use crate::{Error, Metric, MetricType}; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Confidentiality Impact (C) - CVSS v3.1 Base Metric Group /// /// Described in CVSS v3.1 Specification: Section 2.3.1: /// /// /// > This metric measures the impact to the confidentiality of the information /// > resources managed by a software component due to a successfully exploited /// > vulnerability. Confidentiality refers to limiting information access and /// > disclosure to only authorized users, as well as preventing access by, or /// > disclosure to, unauthorized ones. The Base Score is greatest when the loss /// > to the impacted component is highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Confidentiality { /// None (N) /// /// > There is no loss of confidentiality within the impacted component. None, /// Low (L) /// /// > There is some loss of confidentiality. Access to some restricted /// > information is obtained, but the attacker does not have control over /// > what information is obtained, or the amount or kind of loss is /// > limited. The information disclosure does not cause a direct, serious /// > loss to the impacted component. Low, /// High (H) /// /// > There is a total loss of confidentiality, resulting in all resources /// > within the impacted component being divulged to the attacker. /// > Alternatively, access to only some restricted information is /// > obtained, but the disclosed information presents a direct, serious /// > impact. For example, an attacker steals the administrator's password, /// > or private encryption keys of a web server. High, } impl Metric for Confidentiality { const TYPE: MetricType = MetricType::C; fn score(self) -> f64 { match self { Confidentiality::None => 0.0, Confidentiality::Low => 0.22, Confidentiality::High => 0.56, } } fn as_str(self) -> &'static str { match self { Confidentiality::None => "N", Confidentiality::Low => "L", Confidentiality::High => "H", } } } impl fmt::Display for Confidentiality { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for Confidentiality { type Err = Error; fn from_str(s: &str) -> Result { match s { "N" => Ok(Confidentiality::None), "L" => Ok(Confidentiality::Low), "H" => Ok(Confidentiality::High), _ => Err(Error::InvalidMetric { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v3/base/i.rs000064400000000000000000000046451046102023000134430ustar 00000000000000//! Integrity Impact (I) use crate::{Error, Metric, MetricType, Result}; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Integrity Impact (I) - CVSS v3.1 Base Metric Group /// /// Described in CVSS v3.1 Specification: Section 2.3.2: /// /// /// > This metric measures the impact to integrity of a successfully exploited /// > vulnerability. Integrity refers to the trustworthiness and veracity of /// > information. The Base Score is greatest when the consequence to the /// > impacted component is highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Integrity { /// None (N) /// /// > There is no loss of integrity within the impacted component. None, /// Low (L) /// /// > Modification of data is possible, but the attacker does not have /// > control over the consequence of a modification, or the amount of /// > modification is limited. The data modification does not have a /// > direct, serious impact on the impacted component. Low, /// High (H) /// /// > There is a total loss of integrity, or a complete loss of protection. /// > For example, the attacker is able to modify any/all files protected /// > by the impacted component. Alternatively, only some files can be /// > modified, but malicious modification would present a direct, serious /// > consequence to the impacted component. High, } impl Metric for Integrity { const TYPE: MetricType = MetricType::I; fn score(self) -> f64 { match self { Integrity::None => 0.0, Integrity::Low => 0.22, Integrity::High => 0.56, } } fn as_str(self) -> &'static str { match self { Integrity::None => "N", Integrity::Low => "L", Integrity::High => "H", } } } impl fmt::Display for Integrity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for Integrity { type Err = Error; fn from_str(s: &str) -> Result { match s { "N" => Ok(Integrity::None), "L" => Ok(Integrity::Low), "H" => Ok(Integrity::High), _ => Err(Error::InvalidMetric { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v3/base/pr.rs000064400000000000000000000055731046102023000136350ustar 00000000000000//! Privileges Required (PR) use crate::{Metric, MetricType, error::Error}; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Privileges Required (PR) - CVSS v3.1 Base Metric Group /// /// Described in CVSS v3.1 Specification: Section 2.1.3: /// /// /// > This metric describes the level of privileges an attacker must possess /// > *before* successfully exploiting the vulnerability. The Base Score is /// > greatest if no privileges are required. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum PrivilegesRequired { /// High (H) /// /// > The attacker requires privileges that provide significant /// > (e.g., administrative) control over the vulnerable component allowing /// > access to component-wide settings and files. High, /// Low (L) /// /// > The attacker requires privileges that provide basic user capabilities /// > that could normally affect only settings and files owned by a user. /// > Alternatively, an attacker with Low privileges has the ability to /// > access only non-sensitive resources. Low, /// None (N) /// /// > The attacker is unauthorized prior to attack, and therefore does not /// > require any access to settings or files of the vulnerable system /// > to carry out an attack. None, } impl PrivilegesRequired { /// Score when accounting for scope change pub fn scoped_score(self, scope_change: bool) -> f64 { match self { PrivilegesRequired::High => { if scope_change { 0.50 } else { 0.27 } } PrivilegesRequired::Low => { if scope_change { 0.68 } else { 0.62 } } PrivilegesRequired::None => 0.85, } } } impl Metric for PrivilegesRequired { const TYPE: MetricType = MetricType::PR; fn score(self) -> f64 { self.scoped_score(false) } fn as_str(self) -> &'static str { match self { PrivilegesRequired::High => "H", PrivilegesRequired::Low => "L", PrivilegesRequired::None => "N", } } } impl fmt::Display for PrivilegesRequired { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for PrivilegesRequired { type Err = Error; fn from_str(s: &str) -> Result { match s { "H" => Ok(PrivilegesRequired::High), "L" => Ok(PrivilegesRequired::Low), "N" => Ok(PrivilegesRequired::None), _ => Err(Error::InvalidMetric { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v3/base/s.rs000064400000000000000000000067611046102023000134560ustar 00000000000000//! Scope (S) use crate::{Error, Metric, MetricType, Result}; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Scope (S) - CVSS v3.1 Base Metric Group /// /// Described in CVSS v3.1 Specification: Section 2.2: /// /// /// > The Scope metric captures whether a vulnerability in one vulnerable /// > component impacts resources in components beyond its security scope. /// > /// > Formally, a security authority is a mechanism (e.g., an application, /// > an operating system, firmware, a sandbox environment) that defines and /// > enforces access control in terms of how certain subjects/actors /// > (e.g., human users, processes) can access certain restricted /// > objects/resources (e.g., files, CPU, memory) in a controlled manner. /// > All the subjects and objects under the jurisdiction of a single security /// > authority are considered to be under one security scope. If a /// > vulnerability in a vulnerable component can affect a component which is /// > in a different security scope than the vulnerable component, a Scope /// > change occurs. Intuitively, whenever the impact of a vulnerability /// > breaches a security/trust boundary and impacts components outside the /// > security scope in which vulnerable component resides, a Scope change occurs. /// > /// > The security scope of a component encompasses other components that /// > provide functionality solely to that component, even if these other /// > components have their own security authority. For example, a database /// > used solely by one application is considered part of that application’s /// > security scope even if the database has its own security authority, /// > e.g., a mechanism controlling access to database records based on /// > database users and associated database privileges. /// > /// > The Base Score is greatest when a scope change occurs. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Scope { /// Unchanged (U) /// /// > An exploited vulnerability can only affect resources managed by the /// > same security authority. In this case, the vulnerable component and /// > the impacted component are either the same, or both are managed by /// > the same security authority. Unchanged, /// Changed (C) /// /// > An exploited vulnerability can affect resources beyond the security /// > scope managed by the security authority of the vulnerable component. /// > In this case, the vulnerable component and the impacted component /// > are different and managed by different security authorities. Changed, } impl Scope { /// Has the scope changed? pub fn is_changed(self) -> bool { self == Scope::Changed } } impl Metric for Scope { const TYPE: MetricType = MetricType::S; fn score(self) -> f64 { unimplemented!() } fn as_str(self) -> &'static str { match self { Scope::Unchanged => "U", Scope::Changed => "C", } } } impl fmt::Display for Scope { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for Scope { type Err = Error; fn from_str(s: &str) -> Result { match s { "U" => Ok(Scope::Unchanged), "C" => Ok(Scope::Changed), _ => Err(Error::InvalidMetric { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v3/base/ui.rs000064400000000000000000000043041046102023000136200ustar 00000000000000//! User Interaction (UI) use crate::{Error, Metric, MetricType, Result}; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// User Interaction (UI) - CVSS v3.1 Base Metric Group /// /// Described in CVSS v3.1 Specification: Section 2.1.4: /// /// /// > This metric captures the requirement for a human user, other than the /// > attacker, to participate in the successful compromise of the vulnerable /// > component. This metric determines whether the vulnerability can be /// > exploited solely at the will of the attacker, or whether a separate user /// > (or user-initiated process) must participate in some manner. /// > The Base Score is greatest when no user interaction is required. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum UserInteraction { /// Required (R) /// /// > Successful exploitation of this vulnerability requires a user to /// > take some action before the vulnerability can be exploited. For /// > example, a successful exploit may only be possible during the /// > installation of an application by a system administrator. Required, /// None (N) /// /// > The vulnerable system can be exploited without interaction from any user. None, } impl Metric for UserInteraction { const TYPE: MetricType = MetricType::UI; fn score(self) -> f64 { match self { UserInteraction::Required => 0.62, UserInteraction::None => 0.85, } } fn as_str(self) -> &'static str { match self { UserInteraction::Required => "R", UserInteraction::None => "N", } } } impl fmt::Display for UserInteraction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for UserInteraction { type Err = Error; fn from_str(s: &str) -> Result { match s { "R" => Ok(UserInteraction::Required), "N" => Ok(UserInteraction::None), _ => Err(Error::InvalidMetric { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v3/base.rs000064400000000000000000000253701046102023000132110ustar 00000000000000//! CVSS v3.1 Base Metric Group mod a; mod ac; mod av; mod c; mod i; mod pr; mod s; mod ui; pub use self::{ a::Availability, ac::AttackComplexity, av::AttackVector, c::Confidentiality, i::Integrity, pr::PrivilegesRequired, s::Scope, ui::UserInteraction, }; use super::Score; use crate::{Error, Metric, MetricType, PREFIX, Result}; use alloc::{borrow::ToOwned, vec::Vec}; use core::{fmt, str::FromStr}; #[cfg(feature = "serde")] use { alloc::string::{String, ToString}, serde::{Deserialize, Serialize, de, ser}, }; #[cfg(feature = "std")] use crate::Severity; /// CVSS v3.1 Base Metric Group /// /// Described in CVSS v3.1 Specification: Section 2: /// /// /// > The Base metric group represents the intrinsic characteristics of a /// > vulnerability that are constant over time and across user environments. /// > It is composed of two sets of metrics: the Exploitability metrics and /// > the Impact metrics. /// > /// > The Exploitability metrics reflect the ease and technical means by which /// > the vulnerability can be exploited. That is, they represent characteristics /// > of *the thing that is vulnerable*, which we refer to formally as the /// > *vulnerable component*. The Impact metrics reflect the direct consequence /// > of a successful exploit, and represent the consequence to the /// > *thing that suffers the impact*, which we refer to formally as the /// > *impacted component*. /// > /// > While the vulnerable component is typically a software application, /// > module, driver, etc. (or possibly a hardware device), the impacted /// > component could be a software application, a hardware device or a network /// > resource. This potential for measuring the impact of a vulnerability other /// > than the vulnerable component, was a key feature introduced with /// > CVSS v3.0. This property is captured by the Scope metric. #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct Base { /// Minor component of the version pub minor_version: usize, /// Attack Vector (AV) pub av: Option, /// Attack Complexity (AC) pub ac: Option, /// Privileges Required (PR) pub pr: Option, /// User Interaction (UI) pub ui: Option, /// Scope (S) pub s: Option, /// Confidentiality Impact (C) pub c: Option, /// Integrity Impact (I) pub i: Option, /// Availability Impact (A) pub a: Option, } impl Base { /// Calculate Base CVSS score: overall value for determining the severity /// of a vulnerability, generally referred to as the "CVSS score". /// /// Described in CVSS v3.1 Specification: Section 2: /// /// /// > When the Base metrics are assigned values by an analyst, the Base /// > equation computes a score ranging from 0.0 to 10.0. /// > /// > Specifically, the Base equation is derived from two sub equations: /// > the Exploitability sub-score equation, and the Impact sub-score /// > equation. The Exploitability sub-score equation is derived from the /// > Base Exploitability metrics, while the Impact sub-score equation is /// > derived from the Base Impact metrics. #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn score(&self) -> Score { let exploitability = self.exploitability().value(); let iss = self.impact().value(); let iss_scoped = if !self.is_scope_changed() { 6.42 * iss } else { (7.52 * (iss - 0.029)) - (3.25 * (iss - 0.02).powf(15.0)) }; let score = if iss_scoped <= 0.0 { 0.0 } else if !self.is_scope_changed() { (iss_scoped + exploitability).min(10.0) } else { (1.08 * (iss_scoped + exploitability)).min(10.0) }; Score::new(score).roundup() } /// Calculate Base Exploitability score: sub-score for measuring /// ease of exploitation. /// /// Described in CVSS v3.1 Specification: Section 2: /// /// /// > The Exploitability metrics reflect the ease and technical means by which /// > the vulnerability can be exploited. That is, they represent characteristics /// > of *the thing that is vulnerable*, which we refer to formally as the /// > *vulnerable component*. pub fn exploitability(&self) -> Score { let av_score = self.av.map(|av| av.score()).unwrap_or(0.0); let ac_score = self.ac.map(|ac| ac.score()).unwrap_or(0.0); let ui_score = self.ui.map(|ui| ui.score()).unwrap_or(0.0); let pr_score = self .pr .map(|pr| pr.scoped_score(self.is_scope_changed())) .unwrap_or(0.0); (8.22 * av_score * ac_score * pr_score * ui_score).into() } /// Calculate Base Impact Score (ISS): sub-score for measuring the /// consequences of successful exploitation. /// /// Described in CVSS v3.1 Specification: Section 2: /// /// /// > The Impact metrics reflect the direct consequence /// > of a successful exploit, and represent the consequence to the /// > *thing that suffers the impact*, which we refer to formally as the /// > *impacted component*. #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn impact(&self) -> Score { let c_score = self.c.map(|c| c.score()).unwrap_or(0.0); let i_score = self.i.map(|i| i.score()).unwrap_or(0.0); let a_score = self.a.map(|a| a.score()).unwrap_or(0.0); (1.0 - ((1.0 - c_score) * (1.0 - i_score) * (1.0 - a_score)).abs()).into() } /// Iterate over all defined Base metrics pub fn metrics(&self) -> impl Iterator { [ ( MetricType::AV, self.av.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::AC, self.ac.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::PR, self.pr.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::UI, self.ui.as_ref().map(|m| m as &dyn fmt::Debug), ), (MetricType::S, self.s.as_ref().map(|m| m as &dyn fmt::Debug)), (MetricType::C, self.c.as_ref().map(|m| m as &dyn fmt::Debug)), (MetricType::I, self.i.as_ref().map(|m| m as &dyn fmt::Debug)), (MetricType::A, self.a.as_ref().map(|m| m as &dyn fmt::Debug)), ] .into_iter() .filter_map(|(name, metric)| metric.as_ref().map(|&m| (name, m))) } /// Calculate Base CVSS `Severity` according to the /// Qualitative Severity Rating Scale (i.e. Low / Medium / High / Critical) /// /// Described in CVSS v3.1 Specification: Section 5: /// #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn severity(&self) -> Severity { self.score().severity() } /// Has the scope changed? fn is_scope_changed(&self) -> bool { self.s.map(|s| s.is_changed()).unwrap_or(false) } } macro_rules! write_metrics { ($f:expr, $($metric:expr),+) => { $( if let Some(metric) = $metric { write!($f, "/{}", metric)?; } )+ }; } impl fmt::Display for Base { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:3.{}", PREFIX, self.minor_version)?; write_metrics!( f, self.av, self.ac, self.pr, self.ui, self.s, self.c, self.i, self.a ); Ok(()) } } impl FromStr for Base { type Err = Error; fn from_str(s: &str) -> Result { let component_vec = s .split('/') .map(|component| { let mut parts = component.split(':'); let id = parts.next().ok_or_else(|| Error::InvalidComponent { component: component.to_owned(), })?; let value = parts.next().ok_or_else(|| Error::InvalidComponent { component: component.to_owned(), })?; if parts.next().is_some() { return Err(Error::InvalidComponent { component: component.to_owned(), }); } Ok((id, value)) }) .collect::>>()?; let mut components = component_vec.iter(); let &(id, version_string) = components.next().ok_or(Error::InvalidPrefix { prefix: s.to_owned(), })?; if id != PREFIX { return Err(Error::InvalidPrefix { prefix: id.to_owned(), }); } let mut metrics = Self { minor_version: match version_string { "3.0" => 0, "3.1" => 1, _ => { return Err(Error::UnsupportedVersion { version: version_string.to_owned(), }); } }, ..Default::default() }; for &component in components { let id = component.0.to_ascii_uppercase(); let value = component.1.to_ascii_uppercase(); match id.parse::()? { MetricType::AV => metrics.av = Some(value.parse()?), MetricType::AC => metrics.ac = Some(value.parse()?), MetricType::PR => metrics.pr = Some(value.parse()?), MetricType::UI => metrics.ui = Some(value.parse()?), MetricType::S => metrics.s = Some(value.parse()?), MetricType::C => metrics.c = Some(value.parse()?), MetricType::I => metrics.i = Some(value.parse()?), MetricType::A => metrics.a = Some(value.parse()?), } } Ok(metrics) } } #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl<'de> Deserialize<'de> for Base { fn deserialize>( deserializer: D, ) -> core::result::Result { String::deserialize(deserializer)? .parse() .map_err(de::Error::custom) } } #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl Serialize for Base { fn serialize( &self, serializer: S, ) -> core::result::Result { self.to_string().serialize(serializer) } } cvss-2.2.0/src/v3/metric.rs000064400000000000000000000047011046102023000135550ustar 00000000000000//! CVSS v3.0/v3.1 metrics. use crate::{Error, Result}; use alloc::borrow::ToOwned; use core::{ fmt::{self, Debug, Display}, str::FromStr, }; /// Trait for CVSSv3 metrics. pub trait Metric: Copy + Clone + Debug + Display + Eq + FromStr + Ord { /// [`MetricType`] of this metric. const TYPE: MetricType; /// Get the name of this metric. fn name() -> &'static str { Self::TYPE.name() } /// Get CVSS v3.1 score for this metric. fn score(self) -> f64; /// Get `str` describing this metric's value fn as_str(self) -> &'static str; } /// Enum over all of the available CVSSv3 metrics. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[non_exhaustive] pub enum MetricType { /// Availability Impact (A) A, /// Attack Complexity (AC) AC, /// Attack Vector (AV) AV, /// Confidentiality Impact (C) C, /// Integrity Impact (I) I, /// Privileges Required (PR) PR, /// Scope (S) S, /// User Interaction (UI) UI, } impl MetricType { /// Get the name of this metric (i.e. acronym) pub fn name(self) -> &'static str { match self { Self::A => "A", Self::AC => "AC", Self::AV => "AV", Self::C => "C", Self::I => "I", Self::PR => "PR", Self::S => "S", Self::UI => "UI", } } /// Get a description of this metric. pub fn description(self) -> &'static str { match self { Self::A => "Availability Impact", Self::AC => "Attack Complexity", Self::AV => "Attack Vector", Self::C => "Confidentiality Impact", Self::I => "Integrity Impact", Self::PR => "Privileges Required", Self::S => "Scope", Self::UI => "User Interaction", } } } impl Display for MetricType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.name()) } } impl FromStr for MetricType { type Err = Error; fn from_str(s: &str) -> Result { match s { "A" => Ok(Self::A), "AC" => Ok(Self::AC), "AV" => Ok(Self::AV), "C" => Ok(Self::C), "I" => Ok(Self::I), "PR" => Ok(Self::PR), "S" => Ok(Self::S), "UI" => Ok(Self::UI), _ => Err(Error::UnknownMetric { name: s.to_owned() }), } } } cvss-2.2.0/src/v3/score.rs000064400000000000000000000032761046102023000134130ustar 00000000000000//! CVSS v3.1 scores use crate::severity::Severity; /// CVSS v3.1 scores. /// /// Formula described in CVSS v3.1 Specification: Section 5: /// #[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd)] pub struct Score(f64); impl Score { /// Create a new score object pub fn new(score: f64) -> Score { Score(score) } /// Get the score as a floating point value pub fn value(self) -> f64 { self.0 } /// Round the score up to the algorithm described in /// CVSS v3.1: Appendix A - Floating Point Rounding. /// /// #[cfg(feature = "std")] pub fn roundup(self) -> Score { let score_int = (self.0 * 100_000.0) as u64; if score_int % 10000 == 0 { Score((score_int as f64) / 100_000.0) } else { let score_floor = ((score_int as f64) / 10_000.0).floor(); Score((score_floor + 1.0) / 10.0) } } /// Convert the numeric score into a `Severity` pub fn severity(self) -> Severity { if self.0 < 0.1 { Severity::None } else if self.0 < 4.0 { Severity::Low } else if self.0 < 7.0 { Severity::Medium } else if self.0 < 9.0 { Severity::High } else { Severity::Critical } } } impl From for Score { fn from(score: f64) -> Score { Score(score) } } impl From for f64 { fn from(score: Score) -> f64 { score.value() } } impl From for Severity { fn from(score: Score) -> Severity { score.severity() } } cvss-2.2.0/src/v3.rs000064400000000000000000000005531046102023000122730ustar 00000000000000//! Common Vulnerability Scoring System (v3.1) //! //! // TODO(tarcieri): Environmental and Temporal Metrics #[cfg(feature = "v3")] pub mod base; pub mod metric; #[cfg(feature = "v3")] mod score; #[cfg(feature = "v3")] pub use self::{ base::Base, metric::{Metric, MetricType}, score::Score, }; cvss-2.2.0/src/v4/metric/base/ac.rs000064400000000000000000000130361046102023000150540ustar 00000000000000//! Attack Complexity (AC) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Attack Complexity (AC) - CVSS v4.0 Base Metric Group /// /// Described in CVSS v4.0 Specification: Section 2.1.2 /// /// > This metric captures measurable actions that must be taken by the attacker /// > to actively evade or circumvent **existing built-in security-enhancing /// > conditions** in order to obtain a working exploit. These are conditions /// > whose primary purpose is to increase security and/or increase exploit /// > engineering complexity. A vulnerability exploitable without a /// > target-specific variable has a lower complexity than a vulnerability that /// > would require non-trivial customization. This metric is meant to capture /// > security mechanisms utilized by the vulnerable system, and does not relate /// > to the amount of time or attempts it would take for an attacker to /// > succeed, e.g. a race condition. If the attacker does not take action to /// > overcome these conditions, the attack will always fail. /// > /// > The evasion or satisfaction of authentication mechanisms or requisites is /// > included in the Privileges Required assessment and is *not* considered /// > here as a factor of relevance for Attack Complexity. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum AttackComplexity { /// High (H) /// /// > The successful attack depends on the evasion or circumvention of /// > security-enhancing techniques in place that would otherwise hinder the /// > attack. These include: Evasion of exploit mitigation techniques. The /// > attacker must have additional methods available to bypass security /// > measures in place. For example, circumvention of **address space /// > randomization (ASLR) or data execution prevention (DEP)** must be /// > performed for the attack to be successful. Obtaining target-specific /// > secrets. The attacker must gather some **target-specific secret** /// > before the attack can be successful. A secret is any piece of /// > information that cannot be obtained through any amount of /// > reconnaissance. To obtain the secret the attacker must perform /// > additional attacks or break otherwise secure measures (e.g. knowledge /// > of a secret key may be needed to break a crypto channel). This /// > operation must be performed for each attacked target. High, /// Low (L) /// /// > The attacker must take no measurable action to exploit the /// > vulnerability. The attack requires no target-specific circumvention to /// > exploit the vulnerability. An attacker can expect repeatable success /// > against the vulnerable system. Low, } impl Default for AttackComplexity { fn default() -> Self { Self::Low } } impl Metric for AttackComplexity { const TYPE: MetricType = MetricType::AC; fn as_str(self) -> &'static str { match self { AttackComplexity::High => "H", AttackComplexity::Low => "L", } } } impl fmt::Display for AttackComplexity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for AttackComplexity { type Err = Error; fn from_str(s: &str) -> Result { match s { "H" => Ok(AttackComplexity::High), "L" => Ok(AttackComplexity::Low), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{ MetricType, metric::{MetricLevel, environmental::ModifiedAttackComplexity}, }, }; use alloc::borrow::ToOwned; use core::str::FromStr; /// Result of the merging of the base and modified metrics. /// /// Used in scoring. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedAttackComplexity { High, Low, } impl Default for MergedAttackComplexity { fn default() -> Self { Self::Low } } impl FromStr for MergedAttackComplexity { type Err = Error; fn from_str(s: &str) -> Result { match s { "H" => Ok(MergedAttackComplexity::High), "L" => Ok(MergedAttackComplexity::Low), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::AC, value: s.to_owned(), }), } } } impl MetricLevel for MergedAttackComplexity { fn level(self) -> f64 { // AC_levels = {'L': 0.0, 'H': 0.1} match self { Self::High => 0.1, Self::Low => 0.0, } } } impl AttackComplexity { pub(crate) fn merge( self, value: Option, ) -> MergedAttackComplexity { match value { Some(ModifiedAttackComplexity::NotDefined) | None => match self { Self::High => MergedAttackComplexity::High, Self::Low => MergedAttackComplexity::Low, }, Some(ModifiedAttackComplexity::High) => MergedAttackComplexity::High, Some(ModifiedAttackComplexity::Low) => MergedAttackComplexity::Low, } } } } cvss-2.2.0/src/v4/metric/base/at.rs000064400000000000000000000116371046102023000151020ustar 00000000000000//! Attack Requirements (AT) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Attack Requirements (AT) - CVSS v4.0 Base Metric Group /// /// Described in CVSS v4.0 Specification: Section 2.1.3 /// /// > This metric captures the prerequisite **deployment and execution /// > conditions or variables** of the vulnerable system that enable the attack. /// > These differ from security-enhancing techniques/technologies (ref _Attack /// > Complexity_) as the primary purpose of these conditions is **not** to /// > explicitly mitigate attacks, but rather, emerge naturally as a consequence /// > of the deployment and execution of the vulnerable system. If the attacker /// > does not take action to overcome these conditions, the attack may succeed /// > only occasionally or not succeed at all. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum AttackRequirements { /// Present (P) /// /// > The successful attack depends on the presence of specific deployment /// > and execution conditions of the vulnerable system that enable the /// > attack. These include: A **race condition** must be won to /// > successfully exploit the vulnerability. The successfulness of the /// > attack is conditioned on execution conditions that are not under full /// > control of the attacker. The attack may need to be launched multiple /// > times against a single target before being successful. Network /// > injection. The attacker must inject themselves into the logical /// > network path between the target and the resource requested by the /// > victim (e.g. vulnerabilities requiring an on-path attacker). Present, /// None (N) /// /// > The successful attack does not depend on the deployment and execution /// > conditions of the vulnerable system. The attacker can expect to be /// > able to reach the vulnerability and execute the exploit under all or /// > most instances of the vulnerability. None, } impl Default for AttackRequirements { fn default() -> Self { Self::None } } impl Metric for AttackRequirements { const TYPE: MetricType = MetricType::AT; fn as_str(self) -> &'static str { match self { AttackRequirements::Present => "P", AttackRequirements::None => "N", } } } impl fmt::Display for AttackRequirements { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for AttackRequirements { type Err = Error; fn from_str(s: &str) -> Result { match s { "P" => Ok(AttackRequirements::Present), "N" => Ok(AttackRequirements::None), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{ MetricType, metric::{MetricLevel, environmental::ModifiedAttackRequirements}, }, }; use alloc::borrow::ToOwned; use core::str::FromStr; /// Result of the merging of the base and modified metrics. /// /// Used in scoring. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedAttackRequirements { Present, None, } impl Default for MergedAttackRequirements { fn default() -> Self { Self::None } } impl FromStr for MergedAttackRequirements { type Err = Error; fn from_str(s: &str) -> Result { match s { "P" => Ok(MergedAttackRequirements::Present), "N" => Ok(MergedAttackRequirements::None), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::AT, value: s.to_owned(), }), } } } impl MetricLevel for MergedAttackRequirements { fn level(self) -> f64 { // AT_levels = {'N': 0.0, 'P': 0.1} match self { Self::Present => 0.1, Self::None => 0.0, } } } impl AttackRequirements { pub(crate) fn merge( self, value: Option, ) -> MergedAttackRequirements { match value { Some(ModifiedAttackRequirements::NotDefined) | None => match self { Self::Present => MergedAttackRequirements::Present, Self::None => MergedAttackRequirements::None, }, Some(ModifiedAttackRequirements::Present) => MergedAttackRequirements::Present, Some(ModifiedAttackRequirements::None) => MergedAttackRequirements::None, } } } } cvss-2.2.0/src/v4/metric/base/av.rs000064400000000000000000000155171046102023000151050ustar 00000000000000//! Attack Vector (AV) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Attack Vector (AV) - CVSS v4.0 Base Metric Group /// /// Described in CVSS v4.0 Specification: Section 2.1.1 /// /// > This metric reflects the context by which vulnerability exploitation is /// > possible. This metric value (and consequently the resulting severity) will /// > be larger the more remote (logically, and physically) an attacker can be /// > in order to exploit the vulnerable system. The assumption is that the /// > number of potential attackers for a vulnerability that could be exploited /// > from across a network is larger than the number of potential attackers /// > that could exploit a vulnerability requiring physical access to a device, /// > and therefore warrants a greater severity. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum AttackVector { /// Physical (P) /// /// > The attack requires the attacker to physically touch or manipulate the /// > vulnerable system. Physical interaction may be brief (e.g., evil maid /// > attack1) or persistent. An example of such an attack is a cold boot /// > attack in which an attacker gains access to disk encryption keys after /// > physically accessing the target system. Other examples include /// > peripheral attacks via FireWire/USB Direct Memory Access (DMA). Physical, /// Local (L) /// /// > The vulnerable system is not bound to the network stack and the /// > attacker’s path is via read/write/execute capabilities. Either: the /// > attacker exploits the vulnerability by accessing the target system /// > locally (e.g., keyboard, console), or through terminal emulation /// > (e.g., SSH); or the attacker relies on User Interaction by another /// > person to perform actions required to exploit the vulnerability (e.g., /// > using social engineering techniques to trick a legitimate user into /// > opening a malicious document). Local, /// Adjacent (A) /// /// > The vulnerable system is bound to a protocol stack, but the attack is /// > limited at the protocol level to a logically adjacent topology. This /// > can mean an attack must be launched from the same shared proximity /// > (e.g., Bluetooth, NFC, or IEEE 802.11) or logical network (e.g., local /// > IP subnet), or from within a secure or otherwise limited /// > administrative domain (e.g., MPLS, secure VPN within an administrative /// > network zone). One example of an Adjacent attack would be an ARP /// > (IPv4) or neighbor discovery (IPv6) flood leading to a denial of /// > service on the local LAN segment (e.g., CVE-2013-6014). Adjacent, /// Network (N) /// /// > The vulnerable system is bound to the network stack and the set of /// > possible attackers extends beyond the other options listed below, up /// > to and including the entire Internet. Such a vulnerability is often /// > termed “remotely exploitable” and can be thought of as an attack being /// > exploitable at the protocol level one or more network hops away (e.g., /// > across one or more routers). An example of a network attack is an /// > attacker causing a denial of service (DoS) by sending a specially /// > crafted TCP packet across a wide area network (e.g., CVE-2004-0230). Network, } impl Default for AttackVector { fn default() -> Self { Self::Network } } impl Metric for AttackVector { const TYPE: MetricType = MetricType::AV; fn as_str(self) -> &'static str { match self { AttackVector::Network => "N", AttackVector::Adjacent => "A", AttackVector::Local => "L", AttackVector::Physical => "P", } } } impl fmt::Display for AttackVector { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for AttackVector { type Err = Error; fn from_str(s: &str) -> Result { match s { "N" => Ok(AttackVector::Network), "A" => Ok(AttackVector::Adjacent), "L" => Ok(AttackVector::Local), "P" => Ok(AttackVector::Physical), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{ MetricType, metric::{MetricLevel, environmental::ModifiedAttackVector}, }, }; use alloc::borrow::ToOwned; use core::str::FromStr; /// Result of the merging of the base and modified metrics. /// /// Used in scoring. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedAttackVector { Physical, Local, Adjacent, Network, } impl Default for MergedAttackVector { fn default() -> Self { Self::Network } } impl FromStr for MergedAttackVector { type Err = Error; fn from_str(s: &str) -> Result { match s { "P" => Ok(MergedAttackVector::Physical), "L" => Ok(MergedAttackVector::Local), "A" => Ok(MergedAttackVector::Adjacent), "N" => Ok(MergedAttackVector::Network), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::AV, value: s.to_owned(), }), } } } impl MetricLevel for MergedAttackVector { fn level(self) -> f64 { // AV_levels = {"N": 0.0, "A": 0.1, "L": 0.2, "P": 0.3} match self { Self::Physical => 0.3, Self::Local => 0.2, Self::Adjacent => 0.1, Self::Network => 0.0, } } } impl AttackVector { pub(crate) fn merge(self, value: Option) -> MergedAttackVector { match value { Some(ModifiedAttackVector::NotDefined) | None => match self { Self::Network => MergedAttackVector::Network, Self::Adjacent => MergedAttackVector::Adjacent, Self::Local => MergedAttackVector::Local, Self::Physical => MergedAttackVector::Physical, }, Some(ModifiedAttackVector::Network) => MergedAttackVector::Network, Some(ModifiedAttackVector::Adjacent) => MergedAttackVector::Adjacent, Some(ModifiedAttackVector::Local) => MergedAttackVector::Local, Some(ModifiedAttackVector::Physical) => MergedAttackVector::Physical, } } } } cvss-2.2.0/src/v4/metric/base/pr.rs000064400000000000000000000115031046102023000151070ustar 00000000000000//! Privileges Required (PR) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Privileges Required (PR) - CVSS v4.0 Base Metric Group /// /// Described in CVSS v4.0 Specification: Section 2.1.4 /// /// > This metric describes the level of privileges an attacker must possess /// > prior to successfully exploiting the vulnerability. The method by which /// > the attacker obtains privileged credentials prior to the attack (e.g., /// > free trial accounts), is outside the scope of this metric. Generally, /// > self-service provisioned accounts do not constitute a privilege /// > requirement if the attacker can grant themselves privileges as part of the /// > attack. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum PrivilegesRequired { /// High (H) /// /// > The attacker requires privileges that provide significant (e.g., /// > administrative) control over the vulnerable system allowing full /// > access to the vulnerable system’s settings and files. High, /// Low (L) /// /// > The attacker requires privileges that provide basic capabilities that /// > are typically limited to settings and resources owned by a single /// > low-privileged user. Alternatively, an attacker with Low privileges /// > has the ability to access only non-sensitive resources. Low, /// None (N) /// /// > The attacker is unauthenticated prior to attack, and therefore does /// > not require any access to settings or files of the vulnerable system /// > to carry out an attack. None, } impl Default for PrivilegesRequired { fn default() -> Self { Self::None } } impl Metric for PrivilegesRequired { const TYPE: MetricType = MetricType::PR; fn as_str(self) -> &'static str { match self { PrivilegesRequired::None => "N", PrivilegesRequired::Low => "L", PrivilegesRequired::High => "H", } } } impl fmt::Display for PrivilegesRequired { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for PrivilegesRequired { type Err = Error; fn from_str(s: &str) -> Result { match s { "N" => Ok(PrivilegesRequired::None), "L" => Ok(PrivilegesRequired::Low), "H" => Ok(PrivilegesRequired::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{ MetricType, metric::{MetricLevel, environmental::ModifiedPrivilegesRequired}, }, }; use alloc::borrow::ToOwned; use core::str::FromStr; /// Result of the merging of the base and modified metrics. /// /// Used in scoring. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedPrivilegesRequired { High, Low, None, } impl Default for MergedPrivilegesRequired { fn default() -> Self { Self::None } } impl FromStr for MergedPrivilegesRequired { type Err = Error; fn from_str(s: &str) -> Result { match s { "H" => Ok(MergedPrivilegesRequired::High), "L" => Ok(MergedPrivilegesRequired::Low), "N" => Ok(MergedPrivilegesRequired::None), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::PR, value: s.to_owned(), }), } } } impl MetricLevel for MergedPrivilegesRequired { fn level(self) -> f64 { // PR_levels = {"N": 0.0, "L": 0.1, "H": 0.2} match self { Self::High => 0.2, Self::Low => 0.1, Self::None => 0.0, } } } impl PrivilegesRequired { pub(crate) fn merge( self, value: Option, ) -> MergedPrivilegesRequired { match value { Some(ModifiedPrivilegesRequired::NotDefined) | None => match self { Self::High => MergedPrivilegesRequired::High, Self::Low => MergedPrivilegesRequired::Low, Self::None => MergedPrivilegesRequired::None, }, Some(ModifiedPrivilegesRequired::High) => MergedPrivilegesRequired::High, Some(ModifiedPrivilegesRequired::Low) => MergedPrivilegesRequired::Low, Some(ModifiedPrivilegesRequired::None) => MergedPrivilegesRequired::None, } } } } cvss-2.2.0/src/v4/metric/base/sa.rs000064400000000000000000000161471046102023000151020ustar 00000000000000//! Availability Impact to the Subsequent System (SA) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Availability Impact to the Subsequent System (SA) - CVSS v4.0 Base Metric /// Group /// /// Described in CVSS v4.0 Specification: Section 2.2.8 /// /// > This metric measures the impact to the availability of the impacted system /// > resulting from a successfully exploited vulnerability. While the /// > Confidentiality and Integrity impact metrics apply to the loss of /// > confidentiality or integrity of data (e.g., information, files) used by /// > the system, this metric refers to the loss of availability of the impacted /// > system itself, such as a networked service (e.g., web, database, email). /// > Since availability refers to the accessibility of information resources, /// > attacks that consume network bandwidth, processor cycles, or disk space /// > all impact the availability of a system. The resulting score is greatest /// > when the consequence to the system is highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum AvailabilityImpactToTheSubsequentSystem { /// None (N) /// /// > There is no impact to availability within the Subsequent System or all /// > availability impact is constrained to the Vulnerable System. None, /// Low (L) /// /// > Performance is reduced or there are interruptions in resource /// > availability. Even if repeated exploitation of the vulnerability is /// > possible, the attacker does not have the ability to completely deny /// > service to legitimate users. The resources in the Subsequent System /// > are either partially available all of the time, or fully available /// > only some of the time, but overall there is no direct, serious /// > consequence to the Subsequent System. Low, /// High (H) /// /// > There is a total loss of availability, resulting in the attacker being /// > able to fully deny access to resources in the Subsequent System; this /// > loss is either sustained (while the attacker continues to deliver the /// > attack) or persistent (the condition persists even after the attack /// > has completed). Alternatively, the attacker has the ability to deny /// > some availability, but the loss of availability presents a direct, /// > serious consequence to the Subsequent System (e.g., the attacker /// > cannot disrupt existing connections, but can prevent new connections; /// > the attacker can repeatedly exploit a vulnerability that, in each /// > instance of a successful attack, leaks a only small amount of memory, /// > but after repeated exploitation causes a service to become completely /// > unavailable). High, } impl Default for AvailabilityImpactToTheSubsequentSystem { fn default() -> Self { Self::High } } impl Metric for AvailabilityImpactToTheSubsequentSystem { const TYPE: MetricType = MetricType::SA; fn as_str(self) -> &'static str { match self { AvailabilityImpactToTheSubsequentSystem::None => "N", AvailabilityImpactToTheSubsequentSystem::Low => "L", AvailabilityImpactToTheSubsequentSystem::High => "H", } } } impl fmt::Display for AvailabilityImpactToTheSubsequentSystem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for AvailabilityImpactToTheSubsequentSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "N" => Ok(AvailabilityImpactToTheSubsequentSystem::None), "L" => Ok(AvailabilityImpactToTheSubsequentSystem::Low), "H" => Ok(AvailabilityImpactToTheSubsequentSystem::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{ MetricType, metric::{MetricLevel, environmental::ModifiedAvailabilityImpactToTheSubsequentSystem}, }, }; use alloc::borrow::ToOwned; use core::str::FromStr; /// Result of the merging of the base and modified metrics. /// /// Used in scoring. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedAvailabilityImpactToTheSubsequentSystem { Safety, High, Low, None, } impl Default for MergedAvailabilityImpactToTheSubsequentSystem { fn default() -> Self { Self::High } } impl FromStr for MergedAvailabilityImpactToTheSubsequentSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "S" => Ok(MergedAvailabilityImpactToTheSubsequentSystem::Safety), "H" => Ok(MergedAvailabilityImpactToTheSubsequentSystem::High), "L" => Ok(MergedAvailabilityImpactToTheSubsequentSystem::Low), "N" => Ok(MergedAvailabilityImpactToTheSubsequentSystem::None), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::SA, value: s.to_owned(), }), } } } impl MetricLevel for MergedAvailabilityImpactToTheSubsequentSystem { fn level(self) -> f64 { // SA_levels = {'S': 0.0, 'H': 0.1, 'L': 0.2, 'N': 0.3} match self { Self::Safety => 0.0, Self::High => 0.1, Self::Low => 0.2, Self::None => 0.3, } } } impl AvailabilityImpactToTheSubsequentSystem { pub(crate) fn merge( self, value: Option, ) -> MergedAvailabilityImpactToTheSubsequentSystem { match value { Some(ModifiedAvailabilityImpactToTheSubsequentSystem::NotDefined) | None => { match self { Self::High => MergedAvailabilityImpactToTheSubsequentSystem::High, Self::Low => MergedAvailabilityImpactToTheSubsequentSystem::Low, Self::None => MergedAvailabilityImpactToTheSubsequentSystem::None, } } Some(ModifiedAvailabilityImpactToTheSubsequentSystem::High) => { MergedAvailabilityImpactToTheSubsequentSystem::High } Some(ModifiedAvailabilityImpactToTheSubsequentSystem::Low) => { MergedAvailabilityImpactToTheSubsequentSystem::Low } Some(ModifiedAvailabilityImpactToTheSubsequentSystem::Negligible) => { MergedAvailabilityImpactToTheSubsequentSystem::None } Some(ModifiedAvailabilityImpactToTheSubsequentSystem::Safety) => { MergedAvailabilityImpactToTheSubsequentSystem::Safety } } } } } cvss-2.2.0/src/v4/metric/base/sc.rs000064400000000000000000000137731046102023000151060ustar 00000000000000//! Confidentiality Impact to the Subsequent System (SC) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Confidentiality Impact to the Subsequent System (SC) - CVSS v4.0 Base Metric /// Group /// /// Described in CVSS v4.0 Specification: Section 2.2.3 /// /// > This metric measures the impact to the confidentiality of the information /// > managed by the system due to a successfully exploited vulnerability. /// > Confidentiality refers to limiting information access and disclosure to /// > only authorized users, as well as preventing access by, or disclosure to, /// > unauthorized ones. The resulting score is greatest when the loss to the /// > system is highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ConfidentialityImpactToTheSubsequentSystem { /// None (N) /// /// > There is no loss of confidentiality within the Subsequent System or /// > all confidentiality impact is constrained to the Vulnerable System. None, /// Low (L) /// /// > There is some loss of confidentiality. Access to some restricted /// > information is obtained, but the attacker does not have control over /// > what information is obtained, or the amount or kind of loss is /// > limited. The information disclosure does not cause a direct, serious /// > loss to the Subsequent System. Low, /// High (H) /// /// > There is a total loss of confidentiality, resulting in all resources /// > within the Subsequent System being divulged to the attacker. /// > Alternatively, access to only some restricted information is obtained, /// > but the disclosed information presents a direct, serious impact. For /// > example, an attacker steals the administrator's password, or private /// > encryption keys of a web server. High, } impl Default for ConfidentialityImpactToTheSubsequentSystem { fn default() -> Self { Self::High } } impl Metric for ConfidentialityImpactToTheSubsequentSystem { const TYPE: MetricType = MetricType::SC; fn as_str(self) -> &'static str { match self { ConfidentialityImpactToTheSubsequentSystem::None => "N", ConfidentialityImpactToTheSubsequentSystem::Low => "L", ConfidentialityImpactToTheSubsequentSystem::High => "H", } } } impl fmt::Display for ConfidentialityImpactToTheSubsequentSystem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ConfidentialityImpactToTheSubsequentSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "N" => Ok(ConfidentialityImpactToTheSubsequentSystem::None), "L" => Ok(ConfidentialityImpactToTheSubsequentSystem::Low), "H" => Ok(ConfidentialityImpactToTheSubsequentSystem::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{ MetricType, metric::{ MetricLevel, environmental::ModifiedConfidentialityImpactToTheSubsequentSystem, }, }, }; use alloc::borrow::ToOwned; use core::str::FromStr; /// Result of the merging of the base and modified metrics. /// /// Used in scoring. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedConfidentialityImpactToTheSubsequentSystem { High, Low, None, } impl Default for MergedConfidentialityImpactToTheSubsequentSystem { fn default() -> Self { Self::High } } impl FromStr for MergedConfidentialityImpactToTheSubsequentSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "H" => Ok(MergedConfidentialityImpactToTheSubsequentSystem::High), "L" => Ok(MergedConfidentialityImpactToTheSubsequentSystem::Low), "N" => Ok(MergedConfidentialityImpactToTheSubsequentSystem::None), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::SC, value: s.to_owned(), }), } } } impl MetricLevel for MergedConfidentialityImpactToTheSubsequentSystem { fn level(self) -> f64 { // SC_levels = {'H': 0.1, 'L': 0.2, 'N': 0.3} match self { Self::High => 0.1, Self::Low => 0.2, Self::None => 0.3, } } } impl ConfidentialityImpactToTheSubsequentSystem { pub(crate) fn merge( self, value: Option, ) -> MergedConfidentialityImpactToTheSubsequentSystem { match value { Some(ModifiedConfidentialityImpactToTheSubsequentSystem::NotDefined) | None => { match self { Self::High => MergedConfidentialityImpactToTheSubsequentSystem::High, Self::Low => MergedConfidentialityImpactToTheSubsequentSystem::Low, Self::None => MergedConfidentialityImpactToTheSubsequentSystem::None, } } Some(ModifiedConfidentialityImpactToTheSubsequentSystem::High) => { MergedConfidentialityImpactToTheSubsequentSystem::High } Some(ModifiedConfidentialityImpactToTheSubsequentSystem::Low) => { MergedConfidentialityImpactToTheSubsequentSystem::Low } Some(ModifiedConfidentialityImpactToTheSubsequentSystem::Negligible) => { MergedConfidentialityImpactToTheSubsequentSystem::None } } } } } cvss-2.2.0/src/v4/metric/base/si.rs000064400000000000000000000137721046102023000151130ustar 00000000000000//! Integrity Impact to the Subsequent System (SI) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Integrity Impact to the Subsequent System (SI) - CVSS v4.0 Base Metric Group /// /// Described in CVSS v4.0 Specification: Section 2.2.5 /// /// > This metric measures the impact to integrity of a successfully exploited /// > vulnerability. Integrity refers to the trustworthiness and veracity of /// > information. Integrity of a system is impacted when an attacker causes /// > unauthorized modification of system data. Integrity is also impacted when /// > a system user can repudiate critical actions taken in the context of the /// > system (e.g. due to insufficient logging). /// > The resulting score is greatest when the consequence to the system is /// > highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum IntegrityImpactToTheSubsequentSystem { /// None (N) /// /// > There is no loss of integrity within the Subsequent System or all /// > integrity impact is constrained to the Vulnerable System. None, /// Low (L) /// /// > Modification of data is possible, but the attacker does not have /// > control over the consequence of a modification, or the amount of /// > modification is limited. The data modification does not have a direct, /// > serious impact to the Subsequent System. Low, /// High (H) /// /// > There is a total loss of integrity, or a complete loss of protection. /// > For example, the attacker is able to modify any/all files protected by /// > the Subsequent System. Alternatively, only some files can be modified, /// > but malicious modification would present a direct, serious consequence /// > to the Subsequent System. High, } impl Default for IntegrityImpactToTheSubsequentSystem { fn default() -> Self { Self::High } } impl Metric for IntegrityImpactToTheSubsequentSystem { const TYPE: MetricType = MetricType::SI; fn as_str(self) -> &'static str { match self { IntegrityImpactToTheSubsequentSystem::None => "N", IntegrityImpactToTheSubsequentSystem::Low => "L", IntegrityImpactToTheSubsequentSystem::High => "H", } } } impl fmt::Display for IntegrityImpactToTheSubsequentSystem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for IntegrityImpactToTheSubsequentSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "N" => Ok(IntegrityImpactToTheSubsequentSystem::None), "L" => Ok(IntegrityImpactToTheSubsequentSystem::Low), "H" => Ok(IntegrityImpactToTheSubsequentSystem::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{ MetricType, metric::{MetricLevel, environmental::ModifiedIntegrityImpactToTheSubsequentSystem}, }, }; use alloc::borrow::ToOwned; use core::str::FromStr; /// Result of the merging of the base and modified metrics. /// /// Used in scoring. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedIntegrityImpactToTheSubsequentSystem { Safety, High, Low, None, } impl Default for MergedIntegrityImpactToTheSubsequentSystem { fn default() -> Self { Self::High } } impl FromStr for MergedIntegrityImpactToTheSubsequentSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "S" => Ok(MergedIntegrityImpactToTheSubsequentSystem::Safety), "H" => Ok(MergedIntegrityImpactToTheSubsequentSystem::High), "L" => Ok(MergedIntegrityImpactToTheSubsequentSystem::Low), "N" => Ok(MergedIntegrityImpactToTheSubsequentSystem::None), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::SI, value: s.to_owned(), }), } } } impl MetricLevel for MergedIntegrityImpactToTheSubsequentSystem { fn level(self) -> f64 { // SI_levels = {'S': 0.0, 'H': 0.1, 'L': 0.2, 'N': 0.3} match self { Self::Safety => 0.0, Self::High => 0.1, Self::Low => 0.2, Self::None => 0.3, } } } impl IntegrityImpactToTheSubsequentSystem { pub(crate) fn merge( self, value: Option, ) -> MergedIntegrityImpactToTheSubsequentSystem { match value { Some(ModifiedIntegrityImpactToTheSubsequentSystem::NotDefined) | None => match self { Self::High => MergedIntegrityImpactToTheSubsequentSystem::High, Self::Low => MergedIntegrityImpactToTheSubsequentSystem::Low, Self::None => MergedIntegrityImpactToTheSubsequentSystem::None, }, Some(ModifiedIntegrityImpactToTheSubsequentSystem::High) => { MergedIntegrityImpactToTheSubsequentSystem::High } Some(ModifiedIntegrityImpactToTheSubsequentSystem::Low) => { MergedIntegrityImpactToTheSubsequentSystem::Low } Some(ModifiedIntegrityImpactToTheSubsequentSystem::Negligible) => { MergedIntegrityImpactToTheSubsequentSystem::None } Some(ModifiedIntegrityImpactToTheSubsequentSystem::Safety) => { MergedIntegrityImpactToTheSubsequentSystem::Safety } } } } } cvss-2.2.0/src/v4/metric/base/ui.rs000064400000000000000000000134601046102023000151070ustar 00000000000000//! User Interaction (UI) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// User Interaction (UI) - CVSS v4.0 Base Metric Group /// /// Described in CVSS v4.0 Specification: Section 2.1.5 /// /// > This metric captures the requirement for a human user, other than the /// > attacker, to participate in the successful compromise of the vulnerable /// > system. This metric determines whether the vulnerability can be exploited /// > solely at the will of the attacker, or whether a separate user (or /// > user-initiated process) must participate in some manner. The resulting /// > score is greatest when no user interaction is required. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum UserInteraction { /// Active (A) /// /// > Successful exploitation of this vulnerability requires a targeted user /// > to perform specific, conscious interactions with the vulnerable system /// > and the attacker’s payload, or the user’s interactions would actively /// > subvert protection mechanisms which would lead to exploitation of the /// > vulnerability. Examples include: importing a file into a vulnerable /// > system in a specific manner placing files into a specific directory /// > prior to executing code submitting a specific string into a web /// > application (e.g. reflected or self XSS) dismiss or accept prompts or /// > security warnings prior to taking an action (e.g. opening/editing a /// > file, connecting a device). Active, /// Passive (P) /// /// > Successful exploitation of this vulnerability requires limited /// > interaction by the targeted user with the vulnerable system and the /// > attacker’s payload. These interactions would be considered involuntary /// > and do not require that the user actively subvert protections built /// > into the vulnerable system. Examples include: utilizing a website that /// > has been modified to display malicious content when the page is /// > rendered (most stored XSS or CSRF) running an application that calls a /// > malicious binary that has been planted on the system using an /// > application which generates traffic over an untrusted or compromised /// > network (vulnerabilities requiring an on-path attacker) Passive, /// None (N) /// /// > The vulnerable system can be exploited without interaction from any /// > human user, other than the attacker. Examples include: a remote /// > attacker is able to send packets to a target system a locally /// > authenticated attacker executes code to elevate privileges None, } impl Default for UserInteraction { fn default() -> Self { Self::None } } impl Metric for UserInteraction { const TYPE: MetricType = MetricType::UI; fn as_str(self) -> &'static str { match self { UserInteraction::None => "N", UserInteraction::Passive => "P", UserInteraction::Active => "A", } } } impl fmt::Display for UserInteraction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for UserInteraction { type Err = Error; fn from_str(s: &str) -> Result { match s { "N" => Ok(UserInteraction::None), "P" => Ok(UserInteraction::Passive), "A" => Ok(UserInteraction::Active), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{ MetricType, metric::{MetricLevel, environmental::ModifiedUserInteraction}, }, }; use alloc::borrow::ToOwned; use core::str::FromStr; /// Result of the merging of the base and modified metrics. /// /// Used in scoring. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedUserInteraction { Active, Passive, None, } impl Default for MergedUserInteraction { fn default() -> Self { Self::None } } impl FromStr for MergedUserInteraction { type Err = Error; fn from_str(s: &str) -> Result { match s { "A" => Ok(MergedUserInteraction::Active), "P" => Ok(MergedUserInteraction::Passive), "N" => Ok(MergedUserInteraction::None), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::UI, value: s.to_owned(), }), } } } impl MetricLevel for MergedUserInteraction { fn level(self) -> f64 { // UI_levels = {"N": 0.0, "P": 0.1, "A": 0.2} match self { Self::Active => 0.2, Self::Passive => 0.1, Self::None => 0.0, } } } impl UserInteraction { pub(crate) fn merge(self, value: Option) -> MergedUserInteraction { match value { Some(ModifiedUserInteraction::NotDefined) | None => match self { Self::Passive => MergedUserInteraction::Passive, Self::Active => MergedUserInteraction::Active, Self::None => MergedUserInteraction::None, }, Some(ModifiedUserInteraction::Passive) => MergedUserInteraction::Passive, Some(ModifiedUserInteraction::Active) => MergedUserInteraction::Active, Some(ModifiedUserInteraction::None) => MergedUserInteraction::None, } } } } cvss-2.2.0/src/v4/metric/base/va.rs000064400000000000000000000153241046102023000151010ustar 00000000000000//! Availability Impact to the Vulnerable System (VA) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Availability Impact to the Vulnerable System (VA) - CVSS v4.0 Base Metric /// Group /// /// Described in CVSS v4.0 Specification: Section 2.2.7 /// /// > This metric measures the impact to the availability of the impacted system /// > resulting from a successfully exploited vulnerability. While the /// > Confidentiality and Integrity impact metrics apply to the loss of /// > confidentiality or integrity of data (e.g., information, files) used by /// > the system, this metric refers to the loss of availability of the impacted /// > system itself, such as a networked service (e.g., web, database, email). /// > Since availability refers to the accessibility of information resources, /// > attacks that consume network bandwidth, processor cycles, or disk space /// > all impact the availability of a system. The resulting score is greatest /// > when the consequence to the system is highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum AvailabilityImpactToTheVulnerableSystem { /// None (N) /// /// > There is no impact to availability within the Vulnerable System. None, /// Low (L) /// /// > Performance is reduced or there are interruptions in resource /// > availability. Even if repeated exploitation of the vulnerability is /// > possible, the attacker does not have the ability to completely deny /// > service to legitimate users. The resources in the Vulnerable System /// > are either partially available all of the time, or fully available /// > only some of the time, but overall there is no direct, serious /// > consequence to the Vulnerable System. Low, /// High (H) /// /// > There is a total loss of availability, resulting in the attacker being /// > able to fully deny access to resources in the Vulnerable System; this /// > loss is either sustained (while the attacker continues to deliver the /// > attack) or persistent (the condition persists even after the attack /// > has completed). Alternatively, the attacker has the ability to deny /// > some availability, but the loss of availability presents a direct, /// > serious consequence to the Vulnerable System (e.g., the attacker /// > cannot disrupt existing connections, but can prevent new connections; /// > the attacker can repeatedly exploit a vulnerability that, in each /// > instance of a successful attack, leaks a only small amount of memory, /// > but after repeated exploitation causes a service to become completely /// > unavailable). High, } impl Default for AvailabilityImpactToTheVulnerableSystem { fn default() -> Self { Self::High } } impl Metric for AvailabilityImpactToTheVulnerableSystem { const TYPE: MetricType = MetricType::VA; fn as_str(self) -> &'static str { match self { AvailabilityImpactToTheVulnerableSystem::None => "N", AvailabilityImpactToTheVulnerableSystem::Low => "L", AvailabilityImpactToTheVulnerableSystem::High => "H", } } } impl fmt::Display for AvailabilityImpactToTheVulnerableSystem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for AvailabilityImpactToTheVulnerableSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "N" => Ok(AvailabilityImpactToTheVulnerableSystem::None), "L" => Ok(AvailabilityImpactToTheVulnerableSystem::Low), "H" => Ok(AvailabilityImpactToTheVulnerableSystem::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{ MetricType, metric::{MetricLevel, environmental::ModifiedAvailabilityImpactToTheVulnerableSystem}, }, }; use alloc::borrow::ToOwned; use core::str::FromStr; /// Result of the merging of the base and modified metrics. /// /// Used in scoring. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedAvailabilityImpactToTheVulnerableSystem { High, Low, None, } impl Default for MergedAvailabilityImpactToTheVulnerableSystem { fn default() -> Self { Self::High } } impl FromStr for MergedAvailabilityImpactToTheVulnerableSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "H" => Ok(MergedAvailabilityImpactToTheVulnerableSystem::High), "L" => Ok(MergedAvailabilityImpactToTheVulnerableSystem::Low), "N" => Ok(MergedAvailabilityImpactToTheVulnerableSystem::None), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::VA, value: s.to_owned(), }), } } } impl MetricLevel for MergedAvailabilityImpactToTheVulnerableSystem { fn level(self) -> f64 { // VA_levels = {'H': 0.0, 'L': 0.1, 'N': 0.2} match self { Self::High => 0.0, Self::Low => 0.1, Self::None => 0.2, } } } impl AvailabilityImpactToTheVulnerableSystem { pub(crate) fn merge( self, value: Option, ) -> MergedAvailabilityImpactToTheVulnerableSystem { match value { Some(ModifiedAvailabilityImpactToTheVulnerableSystem::NotDefined) | None => { match self { Self::High => MergedAvailabilityImpactToTheVulnerableSystem::High, Self::Low => MergedAvailabilityImpactToTheVulnerableSystem::Low, Self::None => MergedAvailabilityImpactToTheVulnerableSystem::None, } } Some(ModifiedAvailabilityImpactToTheVulnerableSystem::High) => { MergedAvailabilityImpactToTheVulnerableSystem::High } Some(ModifiedAvailabilityImpactToTheVulnerableSystem::Low) => { MergedAvailabilityImpactToTheVulnerableSystem::Low } Some(ModifiedAvailabilityImpactToTheVulnerableSystem::None) => { MergedAvailabilityImpactToTheVulnerableSystem::None } } } } } cvss-2.2.0/src/v4/metric/base/vc.rs000064400000000000000000000136471046102023000151110ustar 00000000000000//! Confidentiality Impact to the Vulnerable System (VC) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Confidentiality Impact to the Vulnerable System (VC) - CVSS v4.0 Base Metric /// Group /// /// Described in CVSS v4.0 Specification: Section 2.2.2 /// /// > This metric measures the impact to the confidentiality of the information /// > managed by the system due to a successfully exploited vulnerability. /// > Confidentiality refers to limiting information access and disclosure to /// > only authorized users, as well as preventing access by, or disclosure to, /// > unauthorized ones. The resulting score is greatest when the loss to the /// > system is highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ConfidentialityImpactToTheVulnerableSystem { /// None (N) /// /// > There is no loss of confidentiality within the Vulnerable System. None, /// Low (L) /// /// > There is some loss of confidentiality. Access to some restricted /// > information is obtained, but the attacker does not have control over /// > what information is obtained, or the amount or kind of loss is /// > limited. The information disclosure does not cause a direct, serious /// > loss to the Vulnerable System. Low, /// High (H) /// /// > There is a total loss of confidentiality, resulting in all information /// > within the Vulnerable System being divulged to the attacker. /// > Alternatively, access to only some restricted information is obtained, /// > but the disclosed information presents a direct, serious impact. For /// > example, an attacker steals the administrator's password, or private /// > encryption keys of a web server. High, } impl Default for ConfidentialityImpactToTheVulnerableSystem { fn default() -> Self { Self::High } } impl Metric for ConfidentialityImpactToTheVulnerableSystem { const TYPE: MetricType = MetricType::VC; fn as_str(self) -> &'static str { match self { ConfidentialityImpactToTheVulnerableSystem::None => "N", ConfidentialityImpactToTheVulnerableSystem::Low => "L", ConfidentialityImpactToTheVulnerableSystem::High => "H", } } } impl fmt::Display for ConfidentialityImpactToTheVulnerableSystem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ConfidentialityImpactToTheVulnerableSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "N" => Ok(ConfidentialityImpactToTheVulnerableSystem::None), "L" => Ok(ConfidentialityImpactToTheVulnerableSystem::Low), "H" => Ok(ConfidentialityImpactToTheVulnerableSystem::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{ MetricType, metric::{ MetricLevel, environmental::ModifiedConfidentialityImpactToTheVulnerableSystem, }, }, }; use alloc::borrow::ToOwned; use core::str::FromStr; /// Result of the merging of the base and modified metrics. /// /// Used in scoring. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedConfidentialityImpactToTheVulnerableSystem { High, Low, None, } impl Default for MergedConfidentialityImpactToTheVulnerableSystem { fn default() -> Self { Self::High } } impl MetricLevel for MergedConfidentialityImpactToTheVulnerableSystem { fn level(self) -> f64 { // VC_levels = {'H': 0.0, 'L': 0.1, 'N': 0.2} match self { Self::High => 0.0, Self::Low => 0.1, Self::None => 0.2, } } } impl FromStr for MergedConfidentialityImpactToTheVulnerableSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "H" => Ok(MergedConfidentialityImpactToTheVulnerableSystem::High), "L" => Ok(MergedConfidentialityImpactToTheVulnerableSystem::Low), "N" => Ok(MergedConfidentialityImpactToTheVulnerableSystem::None), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::VC, value: s.to_owned(), }), } } } impl ConfidentialityImpactToTheVulnerableSystem { pub(crate) fn merge( self, value: Option, ) -> MergedConfidentialityImpactToTheVulnerableSystem { match value { Some(ModifiedConfidentialityImpactToTheVulnerableSystem::NotDefined) | None => { match self { Self::High => MergedConfidentialityImpactToTheVulnerableSystem::High, Self::Low => MergedConfidentialityImpactToTheVulnerableSystem::Low, Self::None => MergedConfidentialityImpactToTheVulnerableSystem::None, } } Some(ModifiedConfidentialityImpactToTheVulnerableSystem::High) => { MergedConfidentialityImpactToTheVulnerableSystem::High } Some(ModifiedConfidentialityImpactToTheVulnerableSystem::Low) => { MergedConfidentialityImpactToTheVulnerableSystem::Low } Some(ModifiedConfidentialityImpactToTheVulnerableSystem::None) => { MergedConfidentialityImpactToTheVulnerableSystem::None } } } } } cvss-2.2.0/src/v4/metric/base/vi.rs000064400000000000000000000131631046102023000151100ustar 00000000000000//! Integrity Impact to the Vulnerable System (VI) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Integrity Impact to the Vulnerable System (VI) - CVSS v4.0 Base Metric Group /// /// Described in CVSS v4.0 Specification: Section 2.2.4 /// /// > This metric measures the impact to integrity of a successfully exploited /// > vulnerability. Integrity refers to the trustworthiness and veracity of /// > information. Integrity of a system is impacted when an attacker causes /// > unauthorized modification of system data. Integrity is also impacted when /// > a system user can repudiate critical actions taken in the context of the /// > system (e.g. due to insufficient logging). /// > The resulting score is greatest when the consequence to the system is /// > highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum IntegrityImpactToTheVulnerableSystem { /// None (N) /// /// > There is no loss of integrity within the Vulnerable System. None, /// Low (L) /// /// > Modification of data is possible, but the attacker does not have /// > control over the consequence of a modification, or the amount of /// > modification is limited. The data modification does not have a direct, /// > serious impact to the Vulnerable System. Low, /// High (H) /// /// > There is a total loss of integrity, or a complete loss of protection. /// > For example, the attacker is able to modify any/all files protected by /// > the Vulnerable System. Alternatively, only some files can be modified, /// > but malicious modification would present a direct, serious consequence /// > to the Vulnerable System. High, } impl Default for IntegrityImpactToTheVulnerableSystem { fn default() -> Self { Self::High } } impl Metric for IntegrityImpactToTheVulnerableSystem { const TYPE: MetricType = MetricType::VI; fn as_str(self) -> &'static str { match self { IntegrityImpactToTheVulnerableSystem::None => "N", IntegrityImpactToTheVulnerableSystem::Low => "L", IntegrityImpactToTheVulnerableSystem::High => "H", } } } impl fmt::Display for IntegrityImpactToTheVulnerableSystem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for IntegrityImpactToTheVulnerableSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "N" => Ok(IntegrityImpactToTheVulnerableSystem::None), "L" => Ok(IntegrityImpactToTheVulnerableSystem::Low), "H" => Ok(IntegrityImpactToTheVulnerableSystem::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{ MetricType, metric::{MetricLevel, environmental::ModifiedIntegrityImpactToTheVulnerableSystem}, }, }; use alloc::borrow::ToOwned; use core::str::FromStr; /// Result of the merging of the base and modified metrics. /// /// Used in scoring. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedIntegrityImpactToTheVulnerableSystem { High, Low, None, } impl Default for MergedIntegrityImpactToTheVulnerableSystem { fn default() -> Self { Self::High } } impl FromStr for MergedIntegrityImpactToTheVulnerableSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "H" => Ok(MergedIntegrityImpactToTheVulnerableSystem::High), "L" => Ok(MergedIntegrityImpactToTheVulnerableSystem::Low), "N" => Ok(MergedIntegrityImpactToTheVulnerableSystem::None), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::VI, value: s.to_owned(), }), } } } impl MetricLevel for MergedIntegrityImpactToTheVulnerableSystem { fn level(self) -> f64 { // VI_levels = {'H': 0.0, 'L': 0.1, 'N': 0.2} match self { Self::High => 0.0, Self::Low => 0.1, Self::None => 0.2, } } } impl IntegrityImpactToTheVulnerableSystem { pub(crate) fn merge( self, value: Option, ) -> MergedIntegrityImpactToTheVulnerableSystem { match value { Some(ModifiedIntegrityImpactToTheVulnerableSystem::NotDefined) | None => match self { Self::High => MergedIntegrityImpactToTheVulnerableSystem::High, Self::Low => MergedIntegrityImpactToTheVulnerableSystem::Low, Self::None => MergedIntegrityImpactToTheVulnerableSystem::None, }, Some(ModifiedIntegrityImpactToTheVulnerableSystem::High) => { MergedIntegrityImpactToTheVulnerableSystem::High } Some(ModifiedIntegrityImpactToTheVulnerableSystem::Low) => { MergedIntegrityImpactToTheVulnerableSystem::Low } Some(ModifiedIntegrityImpactToTheVulnerableSystem::None) => { MergedIntegrityImpactToTheVulnerableSystem::None } } } } } cvss-2.2.0/src/v4/metric/base.rs000064400000000000000000000021701046102023000144660ustar 00000000000000//! CVSS v4.0 Base Metric Group mod ac; mod at; mod av; mod pr; mod sa; mod sc; mod si; mod ui; mod va; mod vc; mod vi; pub use self::{ ac::AttackComplexity, at::AttackRequirements, av::AttackVector, pr::PrivilegesRequired, sa::AvailabilityImpactToTheSubsequentSystem, sc::ConfidentialityImpactToTheSubsequentSystem, si::IntegrityImpactToTheSubsequentSystem, ui::UserInteraction, va::AvailabilityImpactToTheVulnerableSystem, vc::ConfidentialityImpactToTheVulnerableSystem, vi::IntegrityImpactToTheVulnerableSystem, }; #[cfg(feature = "std")] pub(crate) use self::{ ac::merge::MergedAttackComplexity, at::merge::MergedAttackRequirements, av::merge::MergedAttackVector, pr::merge::MergedPrivilegesRequired, sa::merge::MergedAvailabilityImpactToTheSubsequentSystem, sc::merge::MergedConfidentialityImpactToTheSubsequentSystem, si::merge::MergedIntegrityImpactToTheSubsequentSystem, ui::merge::MergedUserInteraction, va::merge::MergedAvailabilityImpactToTheVulnerableSystem, vc::merge::MergedConfidentialityImpactToTheVulnerableSystem, vi::merge::MergedIntegrityImpactToTheVulnerableSystem, }; cvss-2.2.0/src/v4/metric/environmental/ar.rs000064400000000000000000000122261046102023000170420ustar 00000000000000//! Availability Requirements (AR) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Availability Requirements (AR) - CVSS v4.0 Environmental Metric Group /// /// Described in CVSS v4.0 Specification: Section 4.1 /// /// > These metrics enable the consumer to customize the assessment depending on /// > the importance of the affected IT asset to the analyst’s organization, /// > measured in terms of Confidentiality, Integrity, and Availability. That /// > is, if an IT asset supports a business function for which Availability is /// > most important, the analyst can assign a greater value to Availability /// > metrics relative to Confidentiality and Integrity. Each Security /// > Requirement has three possible values: Low, Medium, or High, or the /// > default value of Not Defined (X). /// > /// > The full effect on the environmental score is determined by the /// > corresponding Modified Base Impact metrics. Following the concept of /// > assuming “reasonable worst case”, in absence of explicit values, these /// > metrics are set to the default value of Not Defined (X), which is /// > equivalent to the metric value of High (H). #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum AvailabilityRequirements { /// Not Defined (X) /// /// > This is the default value. Assigning this value indicates there is /// > insufficient information to choose one of the other values. This has /// > the same effect as assigning High as the worst case. NotDefined, /// Low (L) /// /// > Loss of Availability is likely to have only a limited adverse effect /// > on the organization or individuals associated with the organization /// > (e.g., employees, customers). Low, /// Medium (M) /// /// > Loss of Availability is likely to have a serious adverse effect on the /// > organization or individuals associated with the organization (e.g., /// > employees, customers). Medium, /// High (H) /// /// > Loss of Availability is likely to have a catastrophic adverse effect /// > on the organization or individuals associated with the organization /// > (e.g., employees, customers). High, } impl Default for AvailabilityRequirements { fn default() -> Self { Self::NotDefined } } impl Metric for AvailabilityRequirements { const TYPE: MetricType = MetricType::AR; fn as_str(self) -> &'static str { match self { AvailabilityRequirements::NotDefined => "X", AvailabilityRequirements::Low => "L", AvailabilityRequirements::Medium => "M", AvailabilityRequirements::High => "H", } } } impl fmt::Display for AvailabilityRequirements { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for AvailabilityRequirements { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(AvailabilityRequirements::NotDefined), "L" => Ok(AvailabilityRequirements::Low), "M" => Ok(AvailabilityRequirements::Medium), "H" => Ok(AvailabilityRequirements::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{MetricType, metric::MetricLevel}, }; use alloc::borrow::ToOwned; use core::str::FromStr; #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedAvailabilityRequirements { Low, Medium, High, } impl Default for MergedAvailabilityRequirements { fn default() -> Self { Self::High } } impl FromStr for MergedAvailabilityRequirements { type Err = Error; fn from_str(s: &str) -> Result { match s { "L" => Ok(MergedAvailabilityRequirements::Low), "M" => Ok(MergedAvailabilityRequirements::Medium), "H" => Ok(MergedAvailabilityRequirements::High), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::AR, value: s.to_owned(), }), } } } impl AvailabilityRequirements { pub(crate) fn merge(self) -> MergedAvailabilityRequirements { match self { Self::High => MergedAvailabilityRequirements::High, Self::Medium => MergedAvailabilityRequirements::Medium, Self::Low => MergedAvailabilityRequirements::Low, Self::NotDefined => MergedAvailabilityRequirements::High, } } } impl MetricLevel for MergedAvailabilityRequirements { fn level(self) -> f64 { // AR_levels = {'H': 0.0, 'M': 0.1, 'L': 0.2} match self { Self::High => 0.0, Self::Medium => 0.1, Self::Low => 0.2, } } } } cvss-2.2.0/src/v4/metric/environmental/cr.rs000064400000000000000000000123631046102023000170460ustar 00000000000000//! Confidentiality Requirements (CR) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Confidentiality Requirements (CR) - CVSS v4.0 Environmental Metric Group /// /// Described in CVSS v4.0 Specification: Section 4.1 /// /// > These metrics enable the consumer to customize the assessment depending on /// > the importance of the affected IT asset to the analyst’s organization, /// > measured in terms of Confidentiality, Integrity, and Availability. That /// > is, if an IT asset supports a business function for which Availability is /// > most important, the analyst can assign a greater value to Availability /// > metrics relative to Confidentiality and Integrity. Each Security /// > Requirement has three possible values: Low, Medium, or High, or the /// > default value of Not Defined (X). /// > /// > The full effect on the environmental score is determined by the /// > corresponding Modified Base Impact metrics. Following the concept of /// > assuming “reasonable worst case”, in absence of explicit values, these /// > metrics are set to the default value of Not Defined (X), which is /// > equivalent to the metric value of High (H). #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ConfidentialityRequirements { /// Not Defined (X) /// /// > This is the default value. Assigning this value indicates there is /// > insufficient information to choose one of the other values. This has /// > the same effect as assigning High as the worst case. NotDefined, /// Low (L) /// /// > Loss of Confidentiality is likely to have only a limited adverse /// > effect on the organization or individuals associated with the /// > organization (e.g., employees, customers). Low, /// Medium (M) /// /// > Loss of Confidentiality is likely to have a serious adverse effect on /// > the organization or individuals associated with the organization /// > (e.g., employees, customers). Medium, /// High (H) /// /// > Loss of Confidentiality is likely to have a catastrophic adverse /// > effect on the organization or individuals associated with the /// > organization (e.g., employees, customers). High, } impl Default for ConfidentialityRequirements { fn default() -> Self { Self::NotDefined } } impl Metric for ConfidentialityRequirements { const TYPE: MetricType = MetricType::CR; fn as_str(self) -> &'static str { match self { ConfidentialityRequirements::NotDefined => "X", ConfidentialityRequirements::Low => "L", ConfidentialityRequirements::Medium => "M", ConfidentialityRequirements::High => "H", } } } impl fmt::Display for ConfidentialityRequirements { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ConfidentialityRequirements { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ConfidentialityRequirements::NotDefined), "L" => Ok(ConfidentialityRequirements::Low), "M" => Ok(ConfidentialityRequirements::Medium), "H" => Ok(ConfidentialityRequirements::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{MetricType, metric::MetricLevel}, }; use alloc::borrow::ToOwned; use core::str::FromStr; #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedConfidentialityRequirements { Low, Medium, High, } impl Default for MergedConfidentialityRequirements { fn default() -> Self { Self::High } } impl FromStr for MergedConfidentialityRequirements { type Err = Error; fn from_str(s: &str) -> Result { match s { "L" => Ok(MergedConfidentialityRequirements::Low), "M" => Ok(MergedConfidentialityRequirements::Medium), "H" => Ok(MergedConfidentialityRequirements::High), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::CR, value: s.to_owned(), }), } } } impl ConfidentialityRequirements { pub(crate) fn merge(self) -> MergedConfidentialityRequirements { match self { Self::High => MergedConfidentialityRequirements::High, Self::Medium => MergedConfidentialityRequirements::Medium, Self::Low => MergedConfidentialityRequirements::Low, Self::NotDefined => MergedConfidentialityRequirements::High, } } } impl MetricLevel for MergedConfidentialityRequirements { fn level(self) -> f64 { // CR_levels = {'H': 0.0, 'M': 0.1, 'L': 0.2} match self { Self::High => 0.0, Self::Medium => 0.1, Self::Low => 0.2, } } } } cvss-2.2.0/src/v4/metric/environmental/ir.rs000064400000000000000000000121131046102023000170450ustar 00000000000000//! Integrity Requirements (CR) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Integrity Requirements (IR) - CVSS v4.0 Environmental Metric Group /// /// Described in CVSS v4.0 Specification: Section 4.1 /// /// > These metrics enable the consumer to customize the assessment depending on /// > the importance of the affected IT asset to the analyst’s organization, /// > measured in terms of Confidentiality, Integrity, and Availability. That /// > is, if an IT asset supports a business function for which Availability is /// > most important, the analyst can assign a greater value to Availability /// > metrics relative to Confidentiality and Integrity. Each Security /// > Requirement has three possible values: Low, Medium, or High, or the /// > default value of Not Defined (X). /// > /// > The full effect on the environmental score is determined by the /// > corresponding Modified Base Impact metrics. Following the concept of /// > assuming “reasonable worst case”, in absence of explicit values, these /// > metrics are set to the default value of Not Defined (X), which is /// > equivalent to the metric value of High (H). #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum IntegrityRequirements { /// Not Defined (X) /// /// > This is the default value. Assigning this value indicates there is /// > insufficient information to choose one of the other values. This has /// > the same effect as assigning High as the worst case. NotDefined, /// Low (L) /// /// > Loss of Confidentiality is likely to have only a limited adverse /// > effect on the organization or individuals associated with the /// > organization (e.g., employees, customers). Low, /// Medium (M) /// /// > Loss of Confidentiality is likely to have a serious adverse effect on /// > the organization or individuals associated with the organization /// > (e.g., employees, customers). Medium, /// High (H) /// /// > Loss of Confidentiality is likely to have a catastrophic adverse /// > effect on the organization or individuals associated with the /// > organization (e.g., employees, customers). High, } impl Default for IntegrityRequirements { fn default() -> Self { Self::NotDefined } } impl Metric for IntegrityRequirements { const TYPE: MetricType = MetricType::IR; fn as_str(self) -> &'static str { match self { IntegrityRequirements::NotDefined => "X", IntegrityRequirements::Low => "L", IntegrityRequirements::Medium => "M", IntegrityRequirements::High => "H", } } } impl fmt::Display for IntegrityRequirements { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for IntegrityRequirements { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(IntegrityRequirements::NotDefined), "L" => Ok(IntegrityRequirements::Low), "M" => Ok(IntegrityRequirements::Medium), "H" => Ok(IntegrityRequirements::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{MetricType, metric::MetricLevel}, }; use alloc::borrow::ToOwned; use core::str::FromStr; #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedIntegrityRequirements { Low, Medium, High, } impl Default for MergedIntegrityRequirements { fn default() -> Self { Self::High } } impl FromStr for MergedIntegrityRequirements { type Err = Error; fn from_str(s: &str) -> Result { match s { "L" => Ok(MergedIntegrityRequirements::Low), "M" => Ok(MergedIntegrityRequirements::Medium), "H" => Ok(MergedIntegrityRequirements::High), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::IR, value: s.to_owned(), }), } } } impl IntegrityRequirements { pub(crate) fn merge(self) -> MergedIntegrityRequirements { match self { Self::High => MergedIntegrityRequirements::High, Self::Medium => MergedIntegrityRequirements::Medium, Self::Low => MergedIntegrityRequirements::Low, Self::NotDefined => MergedIntegrityRequirements::High, } } } impl MetricLevel for MergedIntegrityRequirements { fn level(self) -> f64 { // IR_levels = {'H': 0.0, 'M': 0.1, 'L': 0.2} match self { Self::High => 0.0, Self::Medium => 0.1, Self::Low => 0.2, } } } } cvss-2.2.0/src/v4/metric/environmental/mac.rs000064400000000000000000000076521046102023000172070ustar 00000000000000//! Modified Attack Complexity (MAC) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Modified Attack Complexity (MAC) - CVSS v4.0 Environmental Metric Group /// /// Described in CVSS v4.0 Specification: Section 4.2 /// /// > This metric captures measurable actions that must be taken by the attacker /// > to actively evade or circumvent **existing built-in security-enhancing /// > conditions** in order to obtain a working exploit. These are conditions /// > whose primary purpose is to increase security and/or increase exploit /// > engineering complexity. A vulnerability exploitable without a /// > target-specific variable has a lower complexity than a vulnerability that /// > would require non-trivial customization. This metric is meant to capture /// > security mechanisms utilized by the vulnerable system, and does not relate /// > to the amount of time or attempts it would take for an attacker to /// > succeed, e.g. a race condition. If the attacker does not take action to /// > overcome these conditions, the attack will always fail. /// > /// > The evasion or satisfaction of authentication mechanisms or requisites is /// > included in the Privileges Required assessment and is *not* considered /// > here as a factor of relevance for Attack Complexity. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ModifiedAttackComplexity { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// High (H) /// /// > The successful attack depends on the evasion or circumvention of /// > security-enhancing techniques in place that would otherwise hinder the /// > attack. These include: Evasion of exploit mitigation techniques. The /// > attacker must have additional methods available to bypass security /// > measures in place. For example, circumvention of **address space /// > randomization (ASLR) or data execution prevention (DEP)** must be /// > performed for the attack to be successful. Obtaining target-specific /// > secrets. The attacker must gather some **target-specific secret** /// > before the attack can be successful. A secret is any piece of /// > information that cannot be obtained through any amount of /// > reconnaissance. To obtain the secret the attacker must perform /// > additional attacks or break otherwise secure measures (e.g. knowledge /// > of a secret key may be needed to break a crypto channel). This /// > operation must be performed for each attacked target. High, /// Low (L) /// /// > The attacker must take no measurable action to exploit the /// > vulnerability. The attack requires no target-specific circumvention to /// > exploit the vulnerability. An attacker can expect repeatable success /// > against the vulnerable system. Low, } impl Default for ModifiedAttackComplexity { fn default() -> Self { Self::NotDefined } } impl Metric for ModifiedAttackComplexity { const TYPE: MetricType = MetricType::MAC; fn as_str(self) -> &'static str { match self { ModifiedAttackComplexity::NotDefined => "X", ModifiedAttackComplexity::High => "H", ModifiedAttackComplexity::Low => "L", } } } impl fmt::Display for ModifiedAttackComplexity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ModifiedAttackComplexity { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ModifiedAttackComplexity::NotDefined), "H" => Ok(ModifiedAttackComplexity::High), "L" => Ok(ModifiedAttackComplexity::Low), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/environmental/mat.rs000064400000000000000000000063351046102023000172250ustar 00000000000000//! Attack Requirements (MAT) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Attack Requirements (MAT) - CVSS v4.0 Environmental Metric Group /// /// Described in CVSS v4.0 Specification: Section 4.2 /// /// > This metric captures the prerequisite **deployment and execution /// > conditions or variables** of the vulnerable system that enable the attack. /// > These differ from security-enhancing techniques/technologies (ref _Attack /// > Complexity_) as the primary purpose of these conditions is **not** to /// > explicitly mitigate attacks, but rather, emerge naturally as a consequence /// > of the deployment and execution of the vulnerable system. If the attacker /// > does not take action to overcome these conditions, the attack may succeed /// > only occasionally or not succeed at all. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ModifiedAttackRequirements { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// Present (P) /// /// > The successful attack depends on the presence of specific deployment /// > and execution conditions of the vulnerable system that enable the /// > attack. These include: A **race condition** must be won to /// > successfully exploit the vulnerability. The successfulness of the /// > attack is conditioned on execution conditions that are not under full /// > control of the attacker. The attack may need to be launched multiple /// > times against a single target before being successful. Network /// > injection. The attacker must inject themselves into the logical /// > network path between the target and the resource requested by the /// > victim (e.g. vulnerabilities requiring an on-path attacker). Present, /// None (N) /// /// > The successful attack does not depend on the deployment and execution /// > conditions of the vulnerable system. The attacker can expect to be /// > able to reach the vulnerability and execute the exploit under all or /// > most instances of the vulnerability. None, } impl Default for ModifiedAttackRequirements { fn default() -> Self { Self::NotDefined } } impl Metric for ModifiedAttackRequirements { const TYPE: MetricType = MetricType::MAT; fn as_str(self) -> &'static str { match self { ModifiedAttackRequirements::NotDefined => "X", ModifiedAttackRequirements::Present => "P", ModifiedAttackRequirements::None => "N", } } } impl fmt::Display for ModifiedAttackRequirements { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ModifiedAttackRequirements { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ModifiedAttackRequirements::NotDefined), "P" => Ok(ModifiedAttackRequirements::Present), "N" => Ok(ModifiedAttackRequirements::None), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/environmental/mav.rs000064400000000000000000000113551046102023000172250ustar 00000000000000//! Attack Vector (MAV) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Attack Vector (MAV) - CVSS v4.0 Environmental Metric Group /// /// Described in CVSS v4.0 Specification: Section 4.2 /// /// > This metric reflects the context by which vulnerability exploitation is /// > possible. This metric value (and consequently the resulting severity) will /// > be larger the more remote (logically, and physically) an attacker can be /// > in order to exploit the vulnerable system. The assumption is that the /// > number of potential attackers for a vulnerability that could be exploited /// > from across a network is larger than the number of potential attackers /// > that could exploit a vulnerability requiring physical access to a device, /// > and therefore warrants a greater severity. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ModifiedAttackVector { /// Physical (P) /// /// > The attack requires the attacker to physically touch or manipulate the /// > vulnerable system. Physical interaction may be brief (e.g., evil maid /// > attack1) or persistent. An example of such an attack is a cold boot /// > attack in which an attacker gains access to disk encryption keys after /// > physically accessing the target system. Other examples include /// > peripheral attacks via FireWire/USB Direct Memory Access (DMA). Physical, /// Local (L) /// /// > The vulnerable system is not bound to the network stack and the /// > attacker’s path is via read/write/execute capabilities. Either: the /// > attacker exploits the vulnerability by accessing the target system /// > locally (e.g., keyboard, console), or through terminal emulation /// > (e.g., SSH); or the attacker relies on User Interaction by another /// > person to perform actions required to exploit the vulnerability (e.g., /// > using social engineering techniques to trick a legitimate user into /// > opening a malicious document). Local, /// Adjacent (A) /// /// > The vulnerable system is bound to a protocol stack, but the attack is /// > limited at the protocol level to a logically adjacent topology. This /// > can mean an attack must be launched from the same shared proximity /// > (e.g., Bluetooth, NFC, or IEEE 802.11) or logical network (e.g., local /// > IP subnet), or from within a secure or otherwise limited /// > administrative domain (e.g., MPLS, secure VPN within an administrative /// > network zone). One example of an Adjacent attack would be an ARP /// > (IPv4) or neighbor discovery (IPv6) flood leading to a denial of /// > service on the local LAN segment (e.g., CVE-2013-6014). Adjacent, /// Network (N) /// /// > The vulnerable system is bound to the network stack and the set of /// > possible attackers extends beyond the other options listed below, up /// > to and including the entire Internet. Such a vulnerability is often /// > termed “remotely exploitable” and can be thought of as an attack being /// > exploitable at the protocol level one or more network hops away (e.g., /// > across one or more routers). An example of a network attack is an /// > attacker causing a denial of service (DoS) by sending a specially /// > crafted TCP packet across a wide area network (e.g., CVE-2004-0230). Network, /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, } impl Default for ModifiedAttackVector { fn default() -> Self { Self::NotDefined } } impl Metric for ModifiedAttackVector { const TYPE: MetricType = MetricType::MAV; fn as_str(self) -> &'static str { match self { ModifiedAttackVector::NotDefined => "X", ModifiedAttackVector::Network => "N", ModifiedAttackVector::Adjacent => "A", ModifiedAttackVector::Local => "L", ModifiedAttackVector::Physical => "P", } } } impl fmt::Display for ModifiedAttackVector { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ModifiedAttackVector { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ModifiedAttackVector::NotDefined), "N" => Ok(ModifiedAttackVector::Network), "A" => Ok(ModifiedAttackVector::Adjacent), "L" => Ok(ModifiedAttackVector::Local), "P" => Ok(ModifiedAttackVector::Physical), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/environmental/mpr.rs000064400000000000000000000056331046102023000172420ustar 00000000000000//! Privileges Required (MPR) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Privileges Required (MPR) - CVSS v4.0 Environmental Metric Group /// /// Described in CVSS v4.0 Specification: Section 4.2 /// /// > This metric describes the level of privileges an attacker must possess /// > prior to successfully exploiting the vulnerability. The method by which /// > the attacker obtains privileged credentials prior to the attack (e.g., /// > free trial accounts), is outside the scope of this metric. Generally, /// > self-service provisioned accounts do not constitute a privilege /// > requirement if the attacker can grant themselves privileges as part of the /// > attack. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ModifiedPrivilegesRequired { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// High (H) /// /// > The attacker requires privileges that provide significant (e.g., /// > administrative) control over the vulnerable system allowing full /// > access to the vulnerable system’s settings and files. High, /// Low (L) /// /// > The attacker requires privileges that provide basic capabilities that /// > are typically limited to settings and resources owned by a single /// > low-privileged user. Alternatively, an attacker with Low privileges /// > has the ability to access only non-sensitive resources. Low, /// None (N) /// /// > The attacker is unauthenticated prior to attack, and therefore does /// > not require any access to settings or files of the vulnerable system /// > to carry out an attack. None, } impl Default for ModifiedPrivilegesRequired { fn default() -> Self { Self::NotDefined } } impl Metric for ModifiedPrivilegesRequired { const TYPE: MetricType = MetricType::MPR; fn as_str(self) -> &'static str { match self { ModifiedPrivilegesRequired::NotDefined => "X", ModifiedPrivilegesRequired::None => "N", ModifiedPrivilegesRequired::Low => "L", ModifiedPrivilegesRequired::High => "H", } } } impl fmt::Display for ModifiedPrivilegesRequired { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ModifiedPrivilegesRequired { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ModifiedPrivilegesRequired::NotDefined), "N" => Ok(ModifiedPrivilegesRequired::None), "L" => Ok(ModifiedPrivilegesRequired::Low), "H" => Ok(ModifiedPrivilegesRequired::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/environmental/msa.rs000064400000000000000000000110221046102023000172110ustar 00000000000000//! Availability Impact to the Subsequent System (MSA) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Availability Impact to the Subsequent System (MSA) - CVSS v4.0 Environmental /// Metric Group /// /// Described in CVSS v4.0 Specification: Section 4.2 /// /// > This metric measures the impact to the availability of the impacted system /// > resulting from a successfully exploited vulnerability. While the /// > Confidentiality and Integrity impact metrics apply to the loss of /// > confidentiality or integrity of data (e.g., information, files) used by /// > the system, this metric refers to the loss of availability of the impacted /// > system itself, such as a networked service (e.g., web, database, email). /// > Since availability refers to the accessibility of information resources, /// > attacks that consume network bandwidth, processor cycles, or disk space /// > all impact the availability of a system. The resulting score is greatest /// > when the consequence to the system is highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ModifiedAvailabilityImpactToTheSubsequentSystem { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// Negligible (N) /// /// > There is no impact to availability within the Subsequent System or all /// > availability impact is constrained to the Vulnerable System. Negligible, /// Low (L) /// /// > Performance is reduced or there are interruptions in resource /// > availability. Even if repeated exploitation of the vulnerability is /// > possible, the attacker does not have the ability to completely deny /// > service to legitimate users. The resources in the Subsequent System /// > are either partially available all of the time, or fully available /// > only some of the time, but overall there is no direct, serious /// > consequence to the Subsequent System. Low, /// High (H) /// /// > There is a total loss of availability, resulting in the attacker being /// > able to fully deny access to resources in the Subsequent System; this /// > loss is either sustained (while the attacker continues to deliver the /// > attack) or persistent (the condition persists even after the attack /// > has completed). Alternatively, the attacker has the ability to deny /// > some availability, but the loss of availability presents a direct, /// > serious consequence to the Subsequent System (e.g., the attacker /// > cannot disrupt existing connections, but can prevent new connections; /// > the attacker can repeatedly exploit a vulnerability that, in each /// > instance of a successful attack, leaks a only small amount of memory, /// > but after repeated exploitation causes a service to become completely /// > unavailable). High, /// Safety (S) Safety, } impl Default for ModifiedAvailabilityImpactToTheSubsequentSystem { fn default() -> Self { Self::NotDefined } } impl Metric for ModifiedAvailabilityImpactToTheSubsequentSystem { const TYPE: MetricType = MetricType::MSA; fn as_str(self) -> &'static str { match self { ModifiedAvailabilityImpactToTheSubsequentSystem::NotDefined => "X", ModifiedAvailabilityImpactToTheSubsequentSystem::Negligible => "N", ModifiedAvailabilityImpactToTheSubsequentSystem::Low => "L", ModifiedAvailabilityImpactToTheSubsequentSystem::High => "H", ModifiedAvailabilityImpactToTheSubsequentSystem::Safety => "S", } } } impl fmt::Display for ModifiedAvailabilityImpactToTheSubsequentSystem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ModifiedAvailabilityImpactToTheSubsequentSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ModifiedAvailabilityImpactToTheSubsequentSystem::NotDefined), "N" => Ok(ModifiedAvailabilityImpactToTheSubsequentSystem::Negligible), "L" => Ok(ModifiedAvailabilityImpactToTheSubsequentSystem::Low), "H" => Ok(ModifiedAvailabilityImpactToTheSubsequentSystem::High), "S" => Ok(ModifiedAvailabilityImpactToTheSubsequentSystem::Safety), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/environmental/msc.rs000064400000000000000000000067221046102023000172260ustar 00000000000000//! Confidentiality Impact to the Subsequent System (MSC) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Confidentiality Impact to the Subsequent System (MSC) - CVSS v4.0 /// Environmental Metric Group /// /// Described in CVSS v4.0 Specification: Section 4.2 /// /// > This metric measures the impact to the confidentiality of the information /// > managed by the system due to a successfully exploited vulnerability. /// > Confidentiality refers to limiting information access and disclosure to /// > only authorized users, as well as preventing access by, or disclosure to, /// > unauthorized ones. The resulting score is greatest when the loss to the /// > system is highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ModifiedConfidentialityImpactToTheSubsequentSystem { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// Negligible (N) /// /// > There is no loss of confidentiality within the Subsequent System or /// > all confidentiality impact is constrained to the Vulnerable System. Negligible, /// Low (L) /// /// > There is some loss of confidentiality. Access to some restricted /// > information is obtained, but the attacker does not have control over /// > what information is obtained, or the amount or kind of loss is /// > limited. The information disclosure does not cause a direct, serious /// > loss to the Subsequent System. Low, /// High (H) /// /// > There is a total loss of confidentiality, resulting in all resources /// > within the Subsequent System being divulged to the attacker. /// > Alternatively, access to only some restricted information is obtained, /// > but the disclosed information presents a direct, serious impact. For /// > example, an attacker steals the administrator's password, or private /// > encryption keys of a web server. High, } impl Default for ModifiedConfidentialityImpactToTheSubsequentSystem { fn default() -> Self { Self::NotDefined } } impl Metric for ModifiedConfidentialityImpactToTheSubsequentSystem { const TYPE: MetricType = MetricType::MSC; fn as_str(self) -> &'static str { match self { ModifiedConfidentialityImpactToTheSubsequentSystem::NotDefined => "X", ModifiedConfidentialityImpactToTheSubsequentSystem::Negligible => "N", ModifiedConfidentialityImpactToTheSubsequentSystem::Low => "L", ModifiedConfidentialityImpactToTheSubsequentSystem::High => "H", } } } impl fmt::Display for ModifiedConfidentialityImpactToTheSubsequentSystem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ModifiedConfidentialityImpactToTheSubsequentSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ModifiedConfidentialityImpactToTheSubsequentSystem::NotDefined), "N" => Ok(ModifiedConfidentialityImpactToTheSubsequentSystem::Negligible), "L" => Ok(ModifiedConfidentialityImpactToTheSubsequentSystem::Low), "H" => Ok(ModifiedConfidentialityImpactToTheSubsequentSystem::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/environmental/msi.rs000064400000000000000000000070141046102023000172270ustar 00000000000000//! Integrity Impact to the Subsequent System (MSI) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Integrity Impact to the Subsequent System (MSI) - CVSS v4.0 Environmental /// Metric Group /// /// Described in CVSS v4.0 Specification: Section 4.2 /// /// > This metric measures the impact to integrity of a successfully exploited /// > vulnerability. Integrity refers to the trustworthiness and veracity of /// > information. Integrity of a system is impacted when an attacker causes /// > unauthorized modification of system data. Integrity is also impacted when /// > a system user can repudiate critical actions taken in the context of the /// > system (e.g. due to insufficient logging). /// > The resulting score is greatest when the consequence to the system is /// > highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ModifiedIntegrityImpactToTheSubsequentSystem { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// Negligible (N) /// /// > There is no loss of integrity within the Subsequent System or all /// > integrity impact is constrained to the Vulnerable System. Negligible, /// Low (L) /// /// > Modification of data is possible, but the attacker does not have /// > control over the consequence of a modification, or the amount of /// > modification is limited. The data modification does not have a direct, /// > serious impact to the Subsequent System. Low, /// High (H) /// /// > There is a total loss of integrity, or a complete loss of protection. /// > For example, the attacker is able to modify any/all files protected by /// > the Subsequent System. Alternatively, only some files can be modified, /// > but malicious modification would present a direct, serious consequence /// > to the Subsequent System. High, /// Safety (S) Safety, } impl Default for ModifiedIntegrityImpactToTheSubsequentSystem { fn default() -> Self { Self::NotDefined } } impl Metric for ModifiedIntegrityImpactToTheSubsequentSystem { const TYPE: MetricType = MetricType::MSI; fn as_str(self) -> &'static str { match self { ModifiedIntegrityImpactToTheSubsequentSystem::NotDefined => "X", ModifiedIntegrityImpactToTheSubsequentSystem::Negligible => "N", ModifiedIntegrityImpactToTheSubsequentSystem::Low => "L", ModifiedIntegrityImpactToTheSubsequentSystem::High => "H", ModifiedIntegrityImpactToTheSubsequentSystem::Safety => "S", } } } impl fmt::Display for ModifiedIntegrityImpactToTheSubsequentSystem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ModifiedIntegrityImpactToTheSubsequentSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ModifiedIntegrityImpactToTheSubsequentSystem::NotDefined), "N" => Ok(ModifiedIntegrityImpactToTheSubsequentSystem::Negligible), "L" => Ok(ModifiedIntegrityImpactToTheSubsequentSystem::Low), "H" => Ok(ModifiedIntegrityImpactToTheSubsequentSystem::High), "S" => Ok(ModifiedIntegrityImpactToTheSubsequentSystem::Safety), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/environmental/mui.rs000064400000000000000000000076671046102023000172470ustar 00000000000000//! User Interaction (MUI) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// User Interaction (MUI) - CVSS v4.0 Environmental Metric Group /// /// Described in CVSS v4.0 Specification: Section 4.2 /// /// > This metric captures the requirement for a human user, other than the /// > attacker, to participate in the successful compromise of the vulnerable /// > system. This metric determines whether the vulnerability can be exploited /// > solely at the will of the attacker, or whether a separate user (or /// > user-initiated process) must participate in some manner. The resulting /// > score is greatest when no user interaction is required. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ModifiedUserInteraction { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// Active (A) /// /// > Successful exploitation of this vulnerability requires a targeted user /// > to perform specific, conscious interactions with the vulnerable system /// > and the attacker’s payload, or the user’s interactions would actively /// > subvert protection mechanisms which would lead to exploitation of the /// > vulnerability. Examples include: importing a file into a vulnerable /// > system in a specific manner placing files into a specific directory /// > prior to executing code submitting a specific string into a web /// > application (e.g. reflected or self XSS) dismiss or accept prompts or /// > security warnings prior to taking an action (e.g. opening/editing a /// > file, connecting a device). Active, /// Passive (P) /// /// > Successful exploitation of this vulnerability requires limited /// > interaction by the targeted user with the vulnerable system and the /// > attacker’s payload. These interactions would be considered involuntary /// > and do not require that the user actively subvert protections built /// > into the vulnerable system. Examples include: utilizing a website that /// > has been modified to display malicious content when the page is /// > rendered (most stored XSS or CSRF) running an application that calls a /// > malicious binary that has been planted on the system using an /// > application which generates traffic over an untrusted or compromised /// > network (vulnerabilities requiring an on-path attacker) Passive, /// None (N) /// /// > The vulnerable system can be exploited without interaction from any /// > human user, other than the attacker. Examples include: a remote /// > attacker is able to send packets to a target system a locally /// > authenticated attacker executes code to elevate privileges None, } impl Default for ModifiedUserInteraction { fn default() -> Self { Self::NotDefined } } impl Metric for ModifiedUserInteraction { const TYPE: MetricType = MetricType::MUI; fn as_str(self) -> &'static str { match self { ModifiedUserInteraction::NotDefined => "X", ModifiedUserInteraction::None => "N", ModifiedUserInteraction::Passive => "P", ModifiedUserInteraction::Active => "A", } } } impl fmt::Display for ModifiedUserInteraction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ModifiedUserInteraction { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ModifiedUserInteraction::NotDefined), "N" => Ok(ModifiedUserInteraction::None), "P" => Ok(ModifiedUserInteraction::Passive), "A" => Ok(ModifiedUserInteraction::Active), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/environmental/mva.rs000064400000000000000000000103561046102023000172250ustar 00000000000000//! Availability Impact to the Vulnerable System (MVA) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Availability Impact to the Vulnerable System (MVA) - CVSS v4.0 Environmental /// Metric Group /// /// Described in CVSS v4.0 Specification: Section 4.2 /// /// > This metric measures the impact to the availability of the impacted system /// > resulting from a successfully exploited vulnerability. While the /// > Confidentiality and Integrity impact metrics apply to the loss of /// > confidentiality or integrity of data (e.g., information, files) used by /// > the system, this metric refers to the loss of availability of the impacted /// > system itself, such as a networked service (e.g., web, database, email). /// > Since availability refers to the accessibility of information resources, /// > attacks that consume network bandwidth, processor cycles, or disk space /// > all impact the availability of a system. The resulting score is greatest /// > when the consequence to the system is highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ModifiedAvailabilityImpactToTheVulnerableSystem { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// None (N) /// /// > There is no impact to availability within the Vulnerable System. None, /// Low (L) /// /// > Performance is reduced or there are interruptions in resource /// > availability. Even if repeated exploitation of the vulnerability is /// > possible, the attacker does not have the ability to completely deny /// > service to legitimate users. The resources in the Vulnerable System /// > are either partially available all of the time, or fully available /// > only some of the time, but overall there is no direct, serious /// > consequence to the Vulnerable System. Low, /// High (H) /// /// > There is a total loss of availability, resulting in the attacker being /// > able to fully deny access to resources in the Vulnerable System; this /// > loss is either sustained (while the attacker continues to deliver the /// > attack) or persistent (the condition persists even after the attack /// > has completed). Alternatively, the attacker has the ability to deny /// > some availability, but the loss of availability presents a direct, /// > serious consequence to the Vulnerable System (e.g., the attacker /// > cannot disrupt existing connections, but can prevent new connections; /// > the attacker can repeatedly exploit a vulnerability that, in each /// > instance of a successful attack, leaks a only small amount of memory, /// > but after repeated exploitation causes a service to become completely /// > unavailable). High, } impl Default for ModifiedAvailabilityImpactToTheVulnerableSystem { fn default() -> Self { Self::NotDefined } } impl Metric for ModifiedAvailabilityImpactToTheVulnerableSystem { const TYPE: MetricType = MetricType::MVA; fn as_str(self) -> &'static str { match self { ModifiedAvailabilityImpactToTheVulnerableSystem::NotDefined => "X", ModifiedAvailabilityImpactToTheVulnerableSystem::None => "N", ModifiedAvailabilityImpactToTheVulnerableSystem::Low => "L", ModifiedAvailabilityImpactToTheVulnerableSystem::High => "H", } } } impl fmt::Display for ModifiedAvailabilityImpactToTheVulnerableSystem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ModifiedAvailabilityImpactToTheVulnerableSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ModifiedAvailabilityImpactToTheVulnerableSystem::NotDefined), "N" => Ok(ModifiedAvailabilityImpactToTheVulnerableSystem::None), "L" => Ok(ModifiedAvailabilityImpactToTheVulnerableSystem::Low), "H" => Ok(ModifiedAvailabilityImpactToTheVulnerableSystem::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/environmental/mvc.rs000064400000000000000000000065511046102023000172310ustar 00000000000000//! Confidentiality Impact to the Vulnerable System (MVC) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Confidentiality Impact to the Vulnerable System (MVC) - CVSS v4.0 /// Environmental Metric Group /// /// Described in CVSS v4.0 Specification: Section 4.2 /// /// > This metric measures the impact to the confidentiality of the information /// > managed by the system due to a successfully exploited vulnerability. /// > Confidentiality refers to limiting information access and disclosure to /// > only authorized users, as well as preventing access by, or disclosure to, /// > unauthorized ones. The resulting score is greatest when the loss to the /// > system is highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ModifiedConfidentialityImpactToTheVulnerableSystem { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// None (N) /// /// > There is no loss of confidentiality within the Vulnerable System. None, /// Low (L) /// /// > There is some loss of confidentiality. Access to some restricted /// > information is obtained, but the attacker does not have control over /// > what information is obtained, or the amount or kind of loss is /// > limited. The information disclosure does not cause a direct, serious /// > loss to the Vulnerable System. Low, /// High (H) /// /// > There is a total loss of confidentiality, resulting in all information /// > within the Vulnerable System being divulged to the attacker. /// > Alternatively, access to only some restricted information is obtained, /// > but the disclosed information presents a direct, serious impact. For /// > example, an attacker steals the administrator's password, or private /// > encryption keys of a web server. High, } impl Default for ModifiedConfidentialityImpactToTheVulnerableSystem { fn default() -> Self { Self::NotDefined } } impl Metric for ModifiedConfidentialityImpactToTheVulnerableSystem { const TYPE: MetricType = MetricType::MVC; fn as_str(self) -> &'static str { match self { ModifiedConfidentialityImpactToTheVulnerableSystem::NotDefined => "X", ModifiedConfidentialityImpactToTheVulnerableSystem::None => "N", ModifiedConfidentialityImpactToTheVulnerableSystem::Low => "L", ModifiedConfidentialityImpactToTheVulnerableSystem::High => "H", } } } impl fmt::Display for ModifiedConfidentialityImpactToTheVulnerableSystem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ModifiedConfidentialityImpactToTheVulnerableSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ModifiedConfidentialityImpactToTheVulnerableSystem::NotDefined), "N" => Ok(ModifiedConfidentialityImpactToTheVulnerableSystem::None), "L" => Ok(ModifiedConfidentialityImpactToTheVulnerableSystem::Low), "H" => Ok(ModifiedConfidentialityImpactToTheVulnerableSystem::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/environmental/mvi.rs000064400000000000000000000063611046102023000172360ustar 00000000000000//! Integrity Impact to the Vulnerable System (MVI) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Integrity Impact to the Vulnerable System (MVI) - CVSS v4.0 Environmental /// Metric Group /// /// Described in CVSS v4.0 Specification: Section 4.2 /// /// > This metric measures the impact to integrity of a successfully exploited /// > vulnerability. Integrity refers to the trustworthiness and veracity of /// > information. Integrity of a system is impacted when an attacker causes /// > unauthorized modification of system data. Integrity is also impacted when /// > a system user can repudiate critical actions taken in the context of the /// > system (e.g. due to insufficient logging). /// > The resulting score is greatest when the consequence to the system is /// > highest. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ModifiedIntegrityImpactToTheVulnerableSystem { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// None (N) /// /// > There is no loss of integrity within the Vulnerable System. None, /// Low (L) /// /// > Modification of data is possible, but the attacker does not have /// > control over the consequence of a modification, or the amount of /// > modification is limited. The data modification does not have a direct, /// > serious impact to the Vulnerable System. Low, /// High (H) /// /// > There is a total loss of integrity, or a complete loss of protection. /// > For example, the attacker is able to modify any/all files protected by /// > the Vulnerable System. Alternatively, only some files can be modified, /// > but malicious modification would present a direct, serious consequence /// > to the Vulnerable System. High, } impl Default for ModifiedIntegrityImpactToTheVulnerableSystem { fn default() -> Self { Self::NotDefined } } impl Metric for ModifiedIntegrityImpactToTheVulnerableSystem { const TYPE: MetricType = MetricType::MVI; fn as_str(self) -> &'static str { match self { ModifiedIntegrityImpactToTheVulnerableSystem::NotDefined => "X", ModifiedIntegrityImpactToTheVulnerableSystem::None => "N", ModifiedIntegrityImpactToTheVulnerableSystem::Low => "L", ModifiedIntegrityImpactToTheVulnerableSystem::High => "H", } } } impl fmt::Display for ModifiedIntegrityImpactToTheVulnerableSystem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ModifiedIntegrityImpactToTheVulnerableSystem { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ModifiedIntegrityImpactToTheVulnerableSystem::NotDefined), "N" => Ok(ModifiedIntegrityImpactToTheVulnerableSystem::None), "L" => Ok(ModifiedIntegrityImpactToTheVulnerableSystem::Low), "H" => Ok(ModifiedIntegrityImpactToTheVulnerableSystem::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/environmental.rs000064400000000000000000000017141046102023000164400ustar 00000000000000//! CVSS v4.0 Environmental Metric Group mod ar; mod cr; mod ir; mod mac; mod mat; mod mav; mod mpr; mod msa; mod msc; mod msi; mod mui; mod mva; mod mvc; mod mvi; pub use self::{ ar::AvailabilityRequirements, cr::ConfidentialityRequirements, ir::IntegrityRequirements, mac::ModifiedAttackComplexity, mat::ModifiedAttackRequirements, mav::ModifiedAttackVector, mpr::ModifiedPrivilegesRequired, msa::ModifiedAvailabilityImpactToTheSubsequentSystem, msc::ModifiedConfidentialityImpactToTheSubsequentSystem, msi::ModifiedIntegrityImpactToTheSubsequentSystem, mui::ModifiedUserInteraction, mva::ModifiedAvailabilityImpactToTheVulnerableSystem, mvc::ModifiedConfidentialityImpactToTheVulnerableSystem, mvi::ModifiedIntegrityImpactToTheVulnerableSystem, }; #[cfg(feature = "std")] pub(crate) use self::{ ar::merge::MergedAvailabilityRequirements, cr::merge::MergedConfidentialityRequirements, ir::merge::MergedIntegrityRequirements, }; cvss-2.2.0/src/v4/metric/supplemental/au.rs000064400000000000000000000042351046102023000166760ustar 00000000000000//! Automatable (AU) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Automatable (AU) - CVSS v4.0 Supplemental Metric Group /// /// Described in CVSS v4.0 Specification: Section 5.2 /// /// > The “Automatable” metric captures the answer to the question ”Can an /// > attacker automate exploitation events for this vulnerability across /// > multiple targets?” based on steps 1-4 of the kill chain [Hutchins et al., /// > 2011]. These steps are reconnaissance, weaponization, delivery, and /// > exploitation. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Automatable { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// Yes (Y) /// /// > Attackers can reliably automate all 4 steps of the kill chain. These /// > steps are reconnaissance, weaponization, delivery, and exploitation /// > (e.g., the vulnerability is “wormable”). Yes, /// No (N) /// /// > Attackers cannot reliably automate all 4 steps of the kill chain for /// > this vulnerability for some reason. These steps are reconnaissance, /// > weaponization, delivery, and exploitation. No, } impl Default for Automatable { fn default() -> Self { Self::NotDefined } } impl Metric for Automatable { const TYPE: MetricType = MetricType::AU; fn as_str(self) -> &'static str { match self { Automatable::NotDefined => "X", Automatable::Yes => "Y", Automatable::No => "N", } } } impl fmt::Display for Automatable { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for Automatable { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(Automatable::NotDefined), "Y" => Ok(Automatable::Yes), "N" => Ok(Automatable::No), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/supplemental/r.rs000064400000000000000000000035771046102023000165420ustar 00000000000000//! Recovery (R) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Recovery (R) - CVSS v4.0 Supplemental Metric Group /// /// Described in CVSS v4.0 Specification: Section 5.4 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Recovery { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// Automatic (A) /// /// > The system recovers services automatically after an attack has been /// > performed. Automatic, /// User (U) /// /// > The system requires manual intervention by the user to recover /// > services, after an attack has been performed. User, /// Irrecoverable (I) /// /// > The system services are irrecoverable by the user, after an attack has /// > been performed. Irrecoverable, } impl Default for Recovery { fn default() -> Self { Self::NotDefined } } impl Metric for Recovery { const TYPE: MetricType = MetricType::R; fn as_str(self) -> &'static str { match self { Recovery::NotDefined => "X", Recovery::Automatic => "A", Recovery::User => "U", Recovery::Irrecoverable => "I", } } } impl fmt::Display for Recovery { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for Recovery { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(Recovery::NotDefined), "A" => Ok(Recovery::Automatic), "U" => Ok(Recovery::User), "I" => Ok(Recovery::Irrecoverable), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/supplemental/re.rs000064400000000000000000000073401046102023000166770ustar 00000000000000//! Vulnerability Response Effort (RE) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Vulnerability Response Effort (RE) - CVSS v4.0 Supplemental Metric Group /// /// Described in CVSS v4.0 Specification: Section 5.6 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum VulnerabilityResponseEffort { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// Low (L) /// /// > The effort required to respond to a vulnerability is low/trivial. /// > Examples include: communication on better documentation, configuration /// > workarounds, or guidance from the vendor that does **not** require an /// > immediate update, upgrade, or replacement by the consuming entity, /// > such as firewall filter configuration. Low, /// Moderate (M) /// /// > The actions required to respond to a vulnerability require some effort /// > on behalf of the consumer and could cause minimal service impact to /// > implement. Examples include: simple remote update, disabling of a /// > subsystem, or a low-touch software upgrade such as a driver update. Moderate, /// High (H) /// /// > The actions required to respond to a vulnerability are significant /// > and/or difficult, and may possibly lead to an extended, scheduled /// > service impact. This would need to be considered for scheduling /// > purposes including honoring any embargo on deployment of the selected /// > response. Alternatively, response to the vulnerability in the field is /// > not possible remotely. The only resolution to the vulnerability /// > involves physical replacement (e.g. units deployed would have to be /// > recalled for a depot level repair or replacement). Examples include: a /// > highly privileged driver update, microcode or UEFI BIOS updates, or /// > software upgrades requiring careful analysis and understanding of any /// > potential infrastructure impact before implementation. A UEFI BIOS /// > update that impacts Trusted Platform Module (TPM) attestation without /// > impacting disk encryption software such as Bit locker is a good recent /// > example. Irreparable failures such as non-bootable flash subsystems, /// > failed disks or solid-state drives (SSD), bad memory modules, network /// > devices, or other non-recoverable under warranty hardware, should also /// > be scored as having a High effort. High, } impl Default for VulnerabilityResponseEffort { fn default() -> Self { Self::NotDefined } } impl Metric for VulnerabilityResponseEffort { const TYPE: MetricType = MetricType::RE; fn as_str(self) -> &'static str { match self { VulnerabilityResponseEffort::NotDefined => "X", VulnerabilityResponseEffort::Low => "L", VulnerabilityResponseEffort::Moderate => "M", VulnerabilityResponseEffort::High => "H", } } } impl fmt::Display for VulnerabilityResponseEffort { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for VulnerabilityResponseEffort { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(VulnerabilityResponseEffort::NotDefined), "L" => Ok(VulnerabilityResponseEffort::Low), "M" => Ok(VulnerabilityResponseEffort::Moderate), "H" => Ok(VulnerabilityResponseEffort::High), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/supplemental/s.rs000064400000000000000000000043641046102023000165360ustar 00000000000000//! Safety (S) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Safety (S) - CVSS v4.0 Supplemental Metric Group /// /// Described in CVSS v4.0 Specification: Section 5.1 /// /// > Like all Supplemental Metrics, providing a value for Safety is completely /// > optional. Suppliers and vendors (AKA: scoring providers) may or may not /// > provide Safety as a Supplemental Metric as they see fit. /// > When a system does have an intended use or fitness of purpose aligned to /// > safety, it is possible that exploiting a vulnerability within that system /// > may have Safety impact which can be represented in the Supplemental /// > Metrics group. Lack of a Safety metric value being supplied does NOT mean /// > that there may not be any Safety-related impacts. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Safety { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// Present (P) /// /// > Consequences of the vulnerability meet definition of IEC 61508 /// > consequence categories of "marginal," "critical," or "catastrophic." Present, /// Negligible (N) /// /// > Consequences of the vulnerability meet definition of IEC 61508 /// > consequence category "negligible." Negligible, } impl Default for Safety { fn default() -> Self { Self::NotDefined } } impl Metric for Safety { const TYPE: MetricType = MetricType::S; fn as_str(self) -> &'static str { match self { Safety::NotDefined => "X", Safety::Present => "P", Safety::Negligible => "N", } } } impl fmt::Display for Safety { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for Safety { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(Safety::NotDefined), "P" => Ok(Safety::Present), "N" => Ok(Safety::Negligible), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/supplemental/u.rs000064400000000000000000000057011046102023000165340ustar 00000000000000//! Provider Urgency (U) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Provider Urgency (U) - CVSS v4.0 Supplemental Metric Group /// /// Described in CVSS v4.0 Specification: Section 5.3 /// /// > Many vendors currently provide supplemental severity ratings to consumers /// > via product security advisories. Other vendors publish Qualitative /// > Severity Ratings from the CVSS Specification Document in their advisories. /// > /// > To facilitate a standardized method to incorporate additional /// > provider-supplied assessment, an optional “pass-through” Supplemental /// > Metric called Provider Urgency is available. /// > /// > Note: While any assessment provider along the product supply chain may /// > provide a Provider Urgency rating: /// > /// > Library Maintainer → OS/Distro Maintainer → Provider 1 … Provider n (PPP) /// > → Consumer /// > /// > The Penultimate Product Provider (PPP) is best positioned to provide a /// > direct assessment of Provider Urgency. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ProviderUrgency { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// Red /// /// > Provider has assessed the impact of this vulnerability as having the /// > highest urgency. Red, /// Amber /// /// > Provider has assessed the impact of this vulnerability as having a /// > moderate urgency. Amber, /// Green /// /// > Provider has assessed the impact of this vulnerability as having a /// > reduced urgency. Green, /// Clear /// /// > Provider has assessed the impact of this vulnerability as having no /// > urgency (Informational). Clear, } impl Default for ProviderUrgency { fn default() -> Self { Self::NotDefined } } impl Metric for ProviderUrgency { const TYPE: MetricType = MetricType::U; fn as_str(self) -> &'static str { match self { ProviderUrgency::NotDefined => "X", ProviderUrgency::Red => "Red", ProviderUrgency::Amber => "Amber", ProviderUrgency::Green => "Green", ProviderUrgency::Clear => "Clear", } } } impl fmt::Display for ProviderUrgency { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ProviderUrgency { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ProviderUrgency::NotDefined), "RED" => Ok(ProviderUrgency::Red), "AMBER" => Ok(ProviderUrgency::Amber), "GREEN" => Ok(ProviderUrgency::Green), "CLEAR" => Ok(ProviderUrgency::Clear), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/supplemental/v.rs000064400000000000000000000041311046102023000165310ustar 00000000000000//! Value Density (V) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Value Density (V) - CVSS v4.0 Supplemental Metric Group /// /// Described in CVSS v4.0 Specification: Section 5.5 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ValueDensity { /// Not Defined (X) /// /// > The metric has not been evaluated. NotDefined, /// Diffuse (D) /// /// > The vulnerable system has limited resources. That is, the resources /// > that the attacker will gain control over with a single exploitation /// > event are relatively small. An example of Diffuse (think: limited) /// > Value Density would be an attack on a single email client /// > vulnerability. Diffuse, /// Concentrated (C) /// /// > The vulnerable system is rich in resources. Heuristically, such /// > systems are often the direct responsibility of “system operators” /// > rather than users. An example of Concentrated (think: broad) Value /// > Density would be an attack on a central email server. Concentrated, } impl Default for ValueDensity { fn default() -> Self { Self::NotDefined } } impl Metric for ValueDensity { const TYPE: MetricType = MetricType::V; fn as_str(self) -> &'static str { match self { ValueDensity::NotDefined => "X", ValueDensity::Diffuse => "D", ValueDensity::Concentrated => "C", } } } impl fmt::Display for ValueDensity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ValueDensity { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ValueDensity::NotDefined), "D" => Ok(ValueDensity::Diffuse), "C" => Ok(ValueDensity::Concentrated), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } cvss-2.2.0/src/v4/metric/supplemental.rs000064400000000000000000000021361046102023000162670ustar 00000000000000//! CVSS v4.0 Supplemental Metric Group //! //! > A new, optional metric group called the Supplemental metric group provides //! > new metrics that describe and measure additional extrinsic attributes of a //! > vulnerability. While the assessment of Supplemental metrics is provisioned //! > by the provider, the usage and response plan of each metric within the //! > Supplemental metric group is determined by the consumer. This contextual //! > information may be employed differently in each consumer’s environment. No //! > metric will have any impact on the final calculated CVSS score (e.g. //! > CVSS-BTE). Organizations may then assign importance and/or effective //! > impact of each metric, or set/combination of metrics, giving them more, //! > less, or absolutely no effect on the final risk analysis. Metrics and //! > values will simply convey additional extrinsic characteristics of the //! > vulnerability itself. mod au; mod r; mod re; mod s; mod u; mod v; pub use self::{ au::Automatable, r::Recovery, re::VulnerabilityResponseEffort, s::Safety, u::ProviderUrgency, v::ValueDensity, }; cvss-2.2.0/src/v4/metric/threat/e.rs000064400000000000000000000147061046102023000152770ustar 00000000000000//! Exploit Maturity (E) use crate::{ Error, Result, v4::metric::{Metric, MetricType}, }; use alloc::borrow::ToOwned; use core::{fmt, str::FromStr}; /// Exploit Maturity (E) - CVSS v4.0 Threat Metric Group /// /// Described in CVSS v4.0 Specification: Section 3.1 /// /// > This metric measures the likelihood of the vulnerability being attacked, /// > and is based on the current state of exploit techniques, exploit code /// > availability, or active, “in-the-wild” exploitation. Public availability /// > of easy-to-use exploit code or exploitation instructions increases the /// > number of potential attackers by including those who are unskilled. /// > Initially, real-world exploitation may only be theoretical. Publication of /// > proof-of-concept exploit code, functional exploit code, or sufficient /// > technical details necessary to exploit the vulnerability may follow. /// > Furthermore, the available exploit code or instructions may progress from /// > a proof-of-concept demonstration to exploit code that is successful in /// > exploiting the vulnerability consistently. In severe cases, it may be /// > delivered as the payload of a network-based worm or virus or other /// > automated attack tools. /// /// > It is the responsibility of the CVSS consumer to populate the values of /// > Exploit Maturity (E) based on information regarding the availability of /// > exploitation code/processes and the state of exploitation techniques. This /// > information will be referred to as “threat intelligence” throughout this /// > document. /// /// > Operational Recommendation: Threat intelligence sources that provide /// > Exploit Maturity information for all vulnerabilities should be preferred /// > over those with only partial coverage. Also, it is recommended to use /// > multiple sources of threat intelligence as many are not comprehensive. /// > This information should be updated as frequently as possible and its /// > application to CVSS assessment should be automated. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ExploitMaturity { /// Not Defined (X) /// /// > Reliable threat intelligence is not available to determine Exploit /// > Maturity characteristics. This is the default value and is equivalent /// > to Attacked (A) for the purposes of the calculation of the score by /// > assuming the worst case. NotDefined, /// Unreported (U) /// /// > Based on available threat intelligence each of the following must /// > apply: No knowledge of publicly available proof-of-concept exploit /// > code No knowledge of reported attempts to exploit this vulnerability /// > No knowledge of publicly available solutions used to simplify attempts /// > to exploit the vulnerability (i.e., neither the “POC” nor “Attacked” /// > values apply) Unreported, /// Proof-of-Concept (P) /// /// > Based on available threat intelligence each of the following must /// > apply: Proof-of-concept exploit code is publicly available No /// > knowledge of reported attempts to exploit this vulnerability No /// > knowledge of publicly available solutions used to simplify attempts to /// > exploit the vulnerability (i.e., the “Attacked” value does not apply) ProofOfConcept, /// Attacked (A) /// /// > Based on available threat intelligence either of the following must /// > apply: Attacks targeting this vulnerability (attempted or successful) /// > have been reported Solutions to simplify attempts to exploit the /// > vulnerability are publicly or privately available (such as exploit /// > toolkits) Attacked, } impl Default for ExploitMaturity { fn default() -> Self { Self::NotDefined } } impl Metric for ExploitMaturity { const TYPE: MetricType = MetricType::E; fn as_str(self) -> &'static str { match self { ExploitMaturity::NotDefined => "X", ExploitMaturity::Attacked => "A", ExploitMaturity::ProofOfConcept => "P", ExploitMaturity::Unreported => "U", } } } impl fmt::Display for ExploitMaturity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", Self::name(), self.as_str()) } } impl FromStr for ExploitMaturity { type Err = Error; fn from_str(s: &str) -> Result { match s { "X" => Ok(ExploitMaturity::NotDefined), "A" => Ok(ExploitMaturity::Attacked), "P" => Ok(ExploitMaturity::ProofOfConcept), "U" => Ok(ExploitMaturity::Unreported), _ => Err(Error::InvalidMetricV4 { metric_type: Self::TYPE, value: s.to_owned(), }), } } } #[cfg(feature = "std")] pub(crate) mod merge { use super::*; use crate::{ Error, v4::{MetricType, metric::MetricLevel}, }; use alloc::borrow::ToOwned; use core::str::FromStr; #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub(crate) enum MergedExploitMaturity { Attacked, ProofOfConcept, Unreported, } impl Default for MergedExploitMaturity { fn default() -> Self { Self::Attacked } } impl FromStr for MergedExploitMaturity { type Err = Error; fn from_str(s: &str) -> Result { match s { "A" => Ok(MergedExploitMaturity::Attacked), "P" => Ok(MergedExploitMaturity::ProofOfConcept), "U" => Ok(MergedExploitMaturity::Unreported), _ => Err(Error::InvalidMetricV4 { metric_type: MetricType::E, value: s.to_owned(), }), } } } impl ExploitMaturity { pub(crate) fn merge(self) -> MergedExploitMaturity { match self { Self::Attacked => MergedExploitMaturity::Attacked, Self::ProofOfConcept => MergedExploitMaturity::ProofOfConcept, Self::Unreported => MergedExploitMaturity::Unreported, Self::NotDefined => MergedExploitMaturity::Attacked, } } } impl MetricLevel for MergedExploitMaturity { fn level(self) -> f64 { // E_levels = {'U': 0.2, 'P': 0.1, 'A': 0} match self { Self::Unreported => 0.2, Self::ProofOfConcept => 0.1, Self::Attacked => 0.0, } } } } cvss-2.2.0/src/v4/metric/threat.rs000064400000000000000000000002331046102023000150410ustar 00000000000000//! CVSS v4.0 Threat Metric Group mod e; pub use self::e::ExploitMaturity; #[cfg(feature = "std")] pub(crate) use self::e::merge::MergedExploitMaturity; cvss-2.2.0/src/v4/metric.rs000064400000000000000000000157631046102023000135700ustar 00000000000000//! CVSS v4 metrics. use crate::{Error, Result}; use alloc::borrow::ToOwned; use core::{ fmt::{self, Debug, Display}, str::FromStr, }; pub mod base; pub mod environmental; pub mod supplemental; pub mod threat; /// Trait for CVSS 4.0 metrics. pub trait Metric: Copy + Clone + Debug + Display + Eq + FromStr + Ord + Default { /// [`MetricType`] of this metric. const TYPE: MetricType; /// Get the name of this metric. fn name() -> &'static str { Self::TYPE.name() } /// Get `str` describing this metric's value fn as_str(self) -> &'static str; } #[cfg(feature = "std")] /// Some metrics have a level associated with them. pub(crate) trait MetricLevel { /// Metric level used in scoring. fn level(self) -> f64; } /// Enum over all of the available metrics. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[non_exhaustive] pub enum MetricType { /// Attack Complexity (AC) AC, /// Attack Requirements (AT) AT, /// Attack Vector (AV) AV, /// Privileges Required (PR) PR, /// Availability Impact to the Subsequent System (SA) SA, /// Confidentiality Impact to the Subsequent System (SC) SC, /// Integrity Impact to the Subsequent System (SI) SI, /// User Interaction (UI) UI, /// Availability Impact to the Vulnerable System (VA) VA, /// Confidentiality Impact to the Vulnerable System (VC) VC, /// Integrity Impact to the Vulnerable System (VI) VI, /// Exploit Maturity (E) E, /// Availability Requirements (AR) AR, /// Confidentiality Requirements (CR) CR, /// Integrity Requirements (IR) IR, /// Modified Attack Complexity (AC) MAC, /// Modified Attack Requirements (MAT) MAT, /// Modified Attack Vector (MAV) MAV, /// Modified Privileges Required (MPR) MPR, /// Modified Availability Impact to the Subsequent System (MSA) MSA, /// Modified Confidentiality Impact to the Subsequent System (MSC) MSC, /// Modified Integrity Impact to the Subsequent System (MSI) MSI, /// Modified User Interaction (MUI) MUI, /// Modified Availability Impact to the Vulnerable System (MVA) MVA, /// Modified Confidentiality Impact to the Vulnerable System (MVC) MVC, /// Modified Integrity Impact to the Vulnerable System (MVI) MVI, /// Automatable (AU) AU, /// Recovery (R) R, /// Vulnerability Response Effort (RE) RE, /// Safety (S) S, /// Provider Urgency (U) U, /// Value Density (V) V, } impl MetricType { /// Get the name of this metric (i.e. acronym) pub fn name(self) -> &'static str { match self { Self::AC => "AC", Self::AT => "AT", Self::AV => "AV", Self::PR => "PR", Self::SA => "SA", Self::SC => "SC", Self::SI => "SI", Self::UI => "UI", Self::VA => "VA", Self::VC => "VC", Self::VI => "VI", Self::E => "E", Self::AR => "AR", Self::CR => "CR", Self::IR => "IR", Self::MAC => "MAC", Self::MAT => "MAT", Self::MAV => "MAV", Self::MPR => "MPR", Self::MSA => "MSA", Self::MSC => "MSC", Self::MSI => "MSI", Self::MUI => "MUI", Self::MVA => "MVA", Self::MVC => "MVC", Self::MVI => "MVI", Self::AU => "AU", Self::R => "R", Self::RE => "RE", Self::S => "S", Self::U => "U", Self::V => "V", } } /// Get a description of this metric. pub fn description(self) -> &'static str { match self { Self::AC => "Attack Complexity", Self::AT => "Attack Requirements", Self::AV => "Attack Vector", Self::PR => "Privileges Required", Self::SA => "Availability Impact to the Subsequent System", Self::SC => "Confidentiality Impact to the Subsequent System", Self::SI => "Integrity Impact to the Subsequent System", Self::UI => "User Interaction", Self::VA => "Availability Impact to the Vulnerable System", Self::VC => "Confidentiality Impact to the Vulnerable System", Self::VI => "Integrity Impact to the Vulnerable System", Self::E => "Exploit Maturity", Self::AR => "Availability Requirements", Self::CR => "Confidentiality Requirements", Self::IR => "Integrity Requirements", Self::MAC => "Modified Attack Complexity", Self::MAT => "Modified Attack Requirements", Self::MAV => "Modified Attack Vector", Self::MPR => "Modified Privileges Required", Self::MSA => "Modified Availability Impact to the Subsequent System", Self::MSC => "Modified Confidentiality Impact to the Subsequent System", Self::MSI => "Modified Integrity Impact to the Subsequent System", Self::MUI => "Modified User Interaction", Self::MVA => "Modified Availability Impact to the Vulnerable System", Self::MVC => "Modified Confidentiality Impact to the Vulnerable System", Self::MVI => "Modified Integrity Impact to the Vulnerable System", Self::AU => "Automatable", Self::R => "Recovery", Self::RE => "Vulnerability Response Effort", Self::S => "Safety", Self::U => "Provider Urgency", Self::V => "Value Density", } } } impl Display for MetricType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.name()) } } impl FromStr for MetricType { type Err = Error; fn from_str(s: &str) -> Result { match s { "AC" => Ok(Self::AC), "AT" => Ok(Self::AT), "AV" => Ok(Self::AV), "PR" => Ok(Self::PR), "SA" => Ok(Self::SA), "SC" => Ok(Self::SC), "SI" => Ok(Self::SI), "UI" => Ok(Self::UI), "VA" => Ok(Self::VA), "VC" => Ok(Self::VC), "VI" => Ok(Self::VI), "E" => Ok(Self::E), "AR" => Ok(Self::AR), "CR" => Ok(Self::CR), "IR" => Ok(Self::IR), "MAC" => Ok(Self::MAC), "MAT" => Ok(Self::MAT), "MAV" => Ok(Self::MAV), "MPR" => Ok(Self::MPR), "MSA" => Ok(Self::MSA), "MSC" => Ok(Self::MSC), "MSI" => Ok(Self::MSI), "MUI" => Ok(Self::MUI), "MVA" => Ok(Self::MVA), "MVC" => Ok(Self::MVC), "MVI" => Ok(Self::MVI), "AU" => Ok(Self::AU), "R" => Ok(Self::R), "RE" => Ok(Self::RE), "S" => Ok(Self::S), "U" => Ok(Self::U), "V" => Ok(Self::V), _ => Err(Error::UnknownMetric { name: s.to_owned() }), } } } cvss-2.2.0/src/v4/score.rs000064400000000000000000000151121046102023000134040ustar 00000000000000//! CVSS v4 scores #[cfg(feature = "std")] use crate::v4::scoring::ScoringVector; use crate::{Error, severity::Severity, v4::Vector}; use alloc::borrow::ToOwned; #[cfg(feature = "serde")] use alloc::string::String; #[cfg(feature = "serde")] use alloc::string::ToString; use core::{fmt, fmt::Display, str::FromStr}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize, de, ser}; /// CVSS v4 scores /// /// It consists of a floating point value, and a nomenclature indicating /// the type of metrics used to calculate the score as recommended by the /// specification. /// /// Described in CVSS v4.0 Specification: Section 1.3 /// /// > This nomenclature should be used wherever a numerical CVSS value is displayed or communicated. #[derive(Clone, Debug, PartialEq)] pub struct Score { value: f64, nomenclature: Nomenclature, } /// > Numerical CVSS Scores have very different meanings based on the metrics /// > used to calculate them. Regarding prioritization, the usefulness of a /// > numerical CVSS score is directly proportional to the CVSS metrics /// > leveraged to generate that score. Therefore, numerical CVSS scores should /// > be labeled using nomenclature that communicates the metrics used in its /// > generation. #[derive(Clone, Debug, PartialEq)] pub enum Nomenclature { ///Base metrics CvssB, /// Base and Environmental metrics CvssBE, /// Base and Threat metrics CvssBT, /// Base, Threat, Environmental metrics CvssBTE, } impl Display for Nomenclature { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::CvssB => write!(f, "CVSS-B"), Self::CvssBE => write!(f, "CVSS-BE"), Self::CvssBT => write!(f, "CVSS-BT"), Self::CvssBTE => write!(f, "CVSS-BTE"), } } } impl FromStr for Nomenclature { type Err = Error; fn from_str(s: &str) -> crate::Result { match s { "CVSS-B" => Ok(Self::CvssB), "CVSS-BE" => Ok(Self::CvssBE), "CVSS-BT" => Ok(Self::CvssBT), "CVSS-BTE" => Ok(Self::CvssBTE), _ => Err(Error::InvalidNomenclatureV4 { nomenclature: s.to_owned(), }), } } } impl From<&Vector> for Nomenclature { fn from(vector: &Vector) -> Self { let has_threat = vector.e.is_some(); let has_environmental = vector.ar.is_some() || vector.cr.is_some() || vector.ir.is_some() || vector.mac.is_some() || vector.mat.is_some() || vector.mav.is_some() || vector.mpr.is_some() || vector.msa.is_some() || vector.msc.is_some() || vector.msi.is_some() || vector.mui.is_some() || vector.mva.is_some() || vector.mvc.is_some() || vector.mvi.is_some(); match (has_threat, has_environmental) { (true, true) => Nomenclature::CvssBTE, (true, false) => Nomenclature::CvssBT, (false, true) => Nomenclature::CvssBE, (false, false) => Nomenclature::CvssB, } } } #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl<'de> Deserialize<'de> for Nomenclature { fn deserialize>(deserializer: D) -> Result { String::deserialize(deserializer)? .parse() .map_err(de::Error::custom) } } #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl Serialize for Nomenclature { fn serialize(&self, serializer: S) -> Result { self.to_string().serialize(serializer) } } #[cfg(feature = "std")] impl From<&Vector> for Score { fn from(vector: &Vector) -> Self { let nomenclature = Nomenclature::from(vector); let scoring = ScoringVector::from(vector); let value = Self::round_v4(scoring.score()); Self { value, nomenclature, } } } impl Score { /// Create a new score pub fn new(value: f64, nomenclature: Nomenclature) -> Self { Self { value, nomenclature, } } /// The specification only states that the score should be rounded to one decimal. /// /// In order to stay compatible with Red Hat's test suite, so use the same /// rounding method. /// /// ```python /// from decimal import Decimal as D, ROUND_HALF_UP /// EPSILON = 10**-6 /// return float(D(x + EPSILON).quantize(D("0.1"), rounding=ROUND_HALF_UP)) /// ``` #[cfg(feature = "std")] pub(crate) fn round_v4(value: f64) -> f64 { let value = f64::clamp(value, 0.0, 10.0); const EPSILON: f64 = 10e-6; ((value + EPSILON) * 10.).round() / 10. } /// Get the score as a floating point value pub fn value(self) -> f64 { self.value } /// Convert the numeric score into a `Severity` pub fn severity(self) -> Severity { if self.value < 0.1 { Severity::None } else if self.value < 4.0 { Severity::Low } else if self.value < 7.0 { Severity::Medium } else if self.value < 9.0 { Severity::High } else { Severity::Critical } } } /// There is no defined or recommended format in the specification, nor in existing implementations. /// /// Using "4.5 (CVSS-BT)". impl Display for Score { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Always show exactly one decimal write!(f, "{:.1} ({})", self.value, self.nomenclature) } } impl From for f64 { fn from(score: Score) -> f64 { score.value() } } impl From for Severity { fn from(score: Score) -> Severity { score.severity() } } #[cfg(test)] mod tests { use super::*; use alloc::string::ToString; #[test] fn new_score() { let score = Score::new(5.5, Nomenclature::CvssB); assert_eq!(score.value(), 5.5); } #[test] #[cfg(feature = "std")] fn round_v4_round() { // 8.6 - 7.15 = 1.4499999999999993 (float) => 1.5 assert_eq!(Score::round_v4(8.6 - 7.15), 1.5); assert_eq!(Score::round_v4(5.12345), 5.1); } #[test] fn into_severity() { let score = Score::new(5.0, Nomenclature::CvssB); let severity: Severity = score.into(); assert_eq!(severity, Severity::Medium); } #[test] fn display_score() { let score = Score::new(4.5, Nomenclature::CvssB); assert_eq!(score.to_string(), "4.5 (CVSS-B)"); } } cvss-2.2.0/src/v4/scoring/lookup.rs000064400000000000000000000261521046102023000152540ustar 00000000000000use crate::v4::scoring::MacroVector; /// `MacroVector` scores /// /// pub(super) fn lookup_global(m: &MacroVector) -> Option { match m.as_tuple() { (0, 0, 0, 0, 0, 0) => Some(10.0), (0, 0, 0, 0, 0, 1) => Some(9.9), (0, 0, 0, 0, 1, 0) => Some(9.8), (0, 0, 0, 0, 1, 1) => Some(9.5), (0, 0, 0, 0, 2, 0) => Some(9.5), (0, 0, 0, 0, 2, 1) => Some(9.2), (0, 0, 0, 1, 0, 0) => Some(10.0), (0, 0, 0, 1, 0, 1) => Some(9.6), (0, 0, 0, 1, 1, 0) => Some(9.3), (0, 0, 0, 1, 1, 1) => Some(8.7), (0, 0, 0, 1, 2, 0) => Some(9.1), (0, 0, 0, 1, 2, 1) => Some(8.1), (0, 0, 0, 2, 0, 0) => Some(9.3), (0, 0, 0, 2, 0, 1) => Some(9.0), (0, 0, 0, 2, 1, 0) => Some(8.9), (0, 0, 0, 2, 1, 1) => Some(8.0), (0, 0, 0, 2, 2, 0) => Some(8.1), (0, 0, 0, 2, 2, 1) => Some(6.8), (0, 0, 1, 0, 0, 0) => Some(9.8), (0, 0, 1, 0, 0, 1) => Some(9.5), (0, 0, 1, 0, 1, 0) => Some(9.5), (0, 0, 1, 0, 1, 1) => Some(9.2), (0, 0, 1, 0, 2, 0) => Some(9.0), (0, 0, 1, 0, 2, 1) => Some(8.4), (0, 0, 1, 1, 0, 0) => Some(9.3), (0, 0, 1, 1, 0, 1) => Some(9.2), (0, 0, 1, 1, 1, 0) => Some(8.9), (0, 0, 1, 1, 1, 1) => Some(8.1), (0, 0, 1, 1, 2, 0) => Some(8.1), (0, 0, 1, 1, 2, 1) => Some(6.5), (0, 0, 1, 2, 0, 0) => Some(8.8), (0, 0, 1, 2, 0, 1) => Some(8.0), (0, 0, 1, 2, 1, 0) => Some(7.8), (0, 0, 1, 2, 1, 1) => Some(7.0), (0, 0, 1, 2, 2, 0) => Some(6.9), (0, 0, 1, 2, 2, 1) => Some(4.8), (0, 0, 2, 0, 0, 1) => Some(9.2), (0, 0, 2, 0, 1, 1) => Some(8.2), (0, 0, 2, 0, 2, 1) => Some(7.2), (0, 0, 2, 1, 0, 1) => Some(7.9), (0, 0, 2, 1, 1, 1) => Some(6.9), (0, 0, 2, 1, 2, 1) => Some(5.0), (0, 0, 2, 2, 0, 1) => Some(6.9), (0, 0, 2, 2, 1, 1) => Some(5.5), (0, 0, 2, 2, 2, 1) => Some(2.7), (0, 1, 0, 0, 0, 0) => Some(9.9), (0, 1, 0, 0, 0, 1) => Some(9.7), (0, 1, 0, 0, 1, 0) => Some(9.5), (0, 1, 0, 0, 1, 1) => Some(9.2), (0, 1, 0, 0, 2, 0) => Some(9.2), (0, 1, 0, 0, 2, 1) => Some(8.5), (0, 1, 0, 1, 0, 0) => Some(9.5), (0, 1, 0, 1, 0, 1) => Some(9.1), (0, 1, 0, 1, 1, 0) => Some(9.0), (0, 1, 0, 1, 1, 1) => Some(8.3), (0, 1, 0, 1, 2, 0) => Some(8.4), (0, 1, 0, 1, 2, 1) => Some(7.1), (0, 1, 0, 2, 0, 0) => Some(9.2), (0, 1, 0, 2, 0, 1) => Some(8.1), (0, 1, 0, 2, 1, 0) => Some(8.2), (0, 1, 0, 2, 1, 1) => Some(7.1), (0, 1, 0, 2, 2, 0) => Some(7.2), (0, 1, 0, 2, 2, 1) => Some(5.3), (0, 1, 1, 0, 0, 0) => Some(9.5), (0, 1, 1, 0, 0, 1) => Some(9.3), (0, 1, 1, 0, 1, 0) => Some(9.2), (0, 1, 1, 0, 1, 1) => Some(8.5), (0, 1, 1, 0, 2, 0) => Some(8.5), (0, 1, 1, 0, 2, 1) => Some(7.3), (0, 1, 1, 1, 0, 0) => Some(9.2), (0, 1, 1, 1, 0, 1) => Some(8.2), (0, 1, 1, 1, 1, 0) => Some(8.0), (0, 1, 1, 1, 1, 1) => Some(7.2), (0, 1, 1, 1, 2, 0) => Some(7.0), (0, 1, 1, 1, 2, 1) => Some(5.9), (0, 1, 1, 2, 0, 0) => Some(8.4), (0, 1, 1, 2, 0, 1) => Some(7.0), (0, 1, 1, 2, 1, 0) => Some(7.1), (0, 1, 1, 2, 1, 1) => Some(5.2), (0, 1, 1, 2, 2, 0) => Some(5.0), (0, 1, 1, 2, 2, 1) => Some(3.0), (0, 1, 2, 0, 0, 1) => Some(8.6), (0, 1, 2, 0, 1, 1) => Some(7.5), (0, 1, 2, 0, 2, 1) => Some(5.2), (0, 1, 2, 1, 0, 1) => Some(7.1), (0, 1, 2, 1, 1, 1) => Some(5.2), (0, 1, 2, 1, 2, 1) => Some(2.9), (0, 1, 2, 2, 0, 1) => Some(6.3), (0, 1, 2, 2, 1, 1) => Some(2.9), (0, 1, 2, 2, 2, 1) => Some(1.7), (1, 0, 0, 0, 0, 0) => Some(9.8), (1, 0, 0, 0, 0, 1) => Some(9.5), (1, 0, 0, 0, 1, 0) => Some(9.4), (1, 0, 0, 0, 1, 1) => Some(8.7), (1, 0, 0, 0, 2, 0) => Some(9.1), (1, 0, 0, 0, 2, 1) => Some(8.1), (1, 0, 0, 1, 0, 0) => Some(9.4), (1, 0, 0, 1, 0, 1) => Some(8.9), (1, 0, 0, 1, 1, 0) => Some(8.6), (1, 0, 0, 1, 1, 1) => Some(7.4), (1, 0, 0, 1, 2, 0) => Some(7.7), (1, 0, 0, 1, 2, 1) => Some(6.4), (1, 0, 0, 2, 0, 0) => Some(8.7), (1, 0, 0, 2, 0, 1) => Some(7.5), (1, 0, 0, 2, 1, 0) => Some(7.4), (1, 0, 0, 2, 1, 1) => Some(6.3), (1, 0, 0, 2, 2, 0) => Some(6.3), (1, 0, 0, 2, 2, 1) => Some(4.9), (1, 0, 1, 0, 0, 0) => Some(9.4), (1, 0, 1, 0, 0, 1) => Some(8.9), (1, 0, 1, 0, 1, 0) => Some(8.8), (1, 0, 1, 0, 1, 1) => Some(7.7), (1, 0, 1, 0, 2, 0) => Some(7.6), (1, 0, 1, 0, 2, 1) => Some(6.7), (1, 0, 1, 1, 0, 0) => Some(8.6), (1, 0, 1, 1, 0, 1) => Some(7.6), (1, 0, 1, 1, 1, 0) => Some(7.4), (1, 0, 1, 1, 1, 1) => Some(5.8), (1, 0, 1, 1, 2, 0) => Some(5.9), (1, 0, 1, 1, 2, 1) => Some(5.0), (1, 0, 1, 2, 0, 0) => Some(7.2), (1, 0, 1, 2, 0, 1) => Some(5.7), (1, 0, 1, 2, 1, 0) => Some(5.7), (1, 0, 1, 2, 1, 1) => Some(5.2), (1, 0, 1, 2, 2, 0) => Some(5.2), (1, 0, 1, 2, 2, 1) => Some(2.5), (1, 0, 2, 0, 0, 1) => Some(8.3), (1, 0, 2, 0, 1, 1) => Some(7.0), (1, 0, 2, 0, 2, 1) => Some(5.4), (1, 0, 2, 1, 0, 1) => Some(6.5), (1, 0, 2, 1, 1, 1) => Some(5.8), (1, 0, 2, 1, 2, 1) => Some(2.6), (1, 0, 2, 2, 0, 1) => Some(5.3), (1, 0, 2, 2, 1, 1) => Some(2.1), (1, 0, 2, 2, 2, 1) => Some(1.3), (1, 1, 0, 0, 0, 0) => Some(9.5), (1, 1, 0, 0, 0, 1) => Some(9.0), (1, 1, 0, 0, 1, 0) => Some(8.8), (1, 1, 0, 0, 1, 1) => Some(7.6), (1, 1, 0, 0, 2, 0) => Some(7.6), (1, 1, 0, 0, 2, 1) => Some(7.0), (1, 1, 0, 1, 0, 0) => Some(9.0), (1, 1, 0, 1, 0, 1) => Some(7.7), (1, 1, 0, 1, 1, 0) => Some(7.5), (1, 1, 0, 1, 1, 1) => Some(6.2), (1, 1, 0, 1, 2, 0) => Some(6.1), (1, 1, 0, 1, 2, 1) => Some(5.3), (1, 1, 0, 2, 0, 0) => Some(7.7), (1, 1, 0, 2, 0, 1) => Some(6.6), (1, 1, 0, 2, 1, 0) => Some(6.8), (1, 1, 0, 2, 1, 1) => Some(5.9), (1, 1, 0, 2, 2, 0) => Some(5.2), (1, 1, 0, 2, 2, 1) => Some(3.0), (1, 1, 1, 0, 0, 0) => Some(8.9), (1, 1, 1, 0, 0, 1) => Some(7.8), (1, 1, 1, 0, 1, 0) => Some(7.6), (1, 1, 1, 0, 1, 1) => Some(6.7), (1, 1, 1, 0, 2, 0) => Some(6.2), (1, 1, 1, 0, 2, 1) => Some(5.8), (1, 1, 1, 1, 0, 0) => Some(7.4), (1, 1, 1, 1, 0, 1) => Some(5.9), (1, 1, 1, 1, 1, 0) => Some(5.7), (1, 1, 1, 1, 1, 1) => Some(5.7), (1, 1, 1, 1, 2, 0) => Some(4.7), (1, 1, 1, 1, 2, 1) => Some(2.3), (1, 1, 1, 2, 0, 0) => Some(6.1), (1, 1, 1, 2, 0, 1) => Some(5.2), (1, 1, 1, 2, 1, 0) => Some(5.7), (1, 1, 1, 2, 1, 1) => Some(2.9), (1, 1, 1, 2, 2, 0) => Some(2.4), (1, 1, 1, 2, 2, 1) => Some(1.6), (1, 1, 2, 0, 0, 1) => Some(7.1), (1, 1, 2, 0, 1, 1) => Some(5.9), (1, 1, 2, 0, 2, 1) => Some(3.0), (1, 1, 2, 1, 0, 1) => Some(5.8), (1, 1, 2, 1, 1, 1) => Some(2.6), (1, 1, 2, 1, 2, 1) => Some(1.5), (1, 1, 2, 2, 0, 1) => Some(2.3), (1, 1, 2, 2, 1, 1) => Some(1.3), (1, 1, 2, 2, 2, 1) => Some(0.6), (2, 0, 0, 0, 0, 0) => Some(9.3), (2, 0, 0, 0, 0, 1) => Some(8.7), (2, 0, 0, 0, 1, 0) => Some(8.6), (2, 0, 0, 0, 1, 1) => Some(7.2), (2, 0, 0, 0, 2, 0) => Some(7.5), (2, 0, 0, 0, 2, 1) => Some(5.8), (2, 0, 0, 1, 0, 0) => Some(8.6), (2, 0, 0, 1, 0, 1) => Some(7.4), (2, 0, 0, 1, 1, 0) => Some(7.4), (2, 0, 0, 1, 1, 1) => Some(6.1), (2, 0, 0, 1, 2, 0) => Some(5.6), (2, 0, 0, 1, 2, 1) => Some(3.4), (2, 0, 0, 2, 0, 0) => Some(7.0), (2, 0, 0, 2, 0, 1) => Some(5.4), (2, 0, 0, 2, 1, 0) => Some(5.2), (2, 0, 0, 2, 1, 1) => Some(4.0), (2, 0, 0, 2, 2, 0) => Some(4.0), (2, 0, 0, 2, 2, 1) => Some(2.2), (2, 0, 1, 0, 0, 0) => Some(8.5), (2, 0, 1, 0, 0, 1) => Some(7.5), (2, 0, 1, 0, 1, 0) => Some(7.4), (2, 0, 1, 0, 1, 1) => Some(5.5), (2, 0, 1, 0, 2, 0) => Some(6.2), (2, 0, 1, 0, 2, 1) => Some(5.1), (2, 0, 1, 1, 0, 0) => Some(7.2), (2, 0, 1, 1, 0, 1) => Some(5.7), (2, 0, 1, 1, 1, 0) => Some(5.5), (2, 0, 1, 1, 1, 1) => Some(4.1), (2, 0, 1, 1, 2, 0) => Some(4.6), (2, 0, 1, 1, 2, 1) => Some(1.9), (2, 0, 1, 2, 0, 0) => Some(5.3), (2, 0, 1, 2, 0, 1) => Some(3.6), (2, 0, 1, 2, 1, 0) => Some(3.4), (2, 0, 1, 2, 1, 1) => Some(1.9), (2, 0, 1, 2, 2, 0) => Some(1.9), (2, 0, 1, 2, 2, 1) => Some(0.8), (2, 0, 2, 0, 0, 1) => Some(6.4), (2, 0, 2, 0, 1, 1) => Some(5.1), (2, 0, 2, 0, 2, 1) => Some(2.0), (2, 0, 2, 1, 0, 1) => Some(4.7), (2, 0, 2, 1, 1, 1) => Some(2.1), (2, 0, 2, 1, 2, 1) => Some(1.1), (2, 0, 2, 2, 0, 1) => Some(2.4), (2, 0, 2, 2, 1, 1) => Some(0.9), (2, 0, 2, 2, 2, 1) => Some(0.4), (2, 1, 0, 0, 0, 0) => Some(8.8), (2, 1, 0, 0, 0, 1) => Some(7.5), (2, 1, 0, 0, 1, 0) => Some(7.3), (2, 1, 0, 0, 1, 1) => Some(5.3), (2, 1, 0, 0, 2, 0) => Some(6.0), (2, 1, 0, 0, 2, 1) => Some(5.0), (2, 1, 0, 1, 0, 0) => Some(7.3), (2, 1, 0, 1, 0, 1) => Some(5.5), (2, 1, 0, 1, 1, 0) => Some(5.9), (2, 1, 0, 1, 1, 1) => Some(4.0), (2, 1, 0, 1, 2, 0) => Some(4.1), (2, 1, 0, 1, 2, 1) => Some(2.0), (2, 1, 0, 2, 0, 0) => Some(5.4), (2, 1, 0, 2, 0, 1) => Some(4.3), (2, 1, 0, 2, 1, 0) => Some(4.5), (2, 1, 0, 2, 1, 1) => Some(2.2), (2, 1, 0, 2, 2, 0) => Some(2.0), (2, 1, 0, 2, 2, 1) => Some(1.1), (2, 1, 1, 0, 0, 0) => Some(7.5), (2, 1, 1, 0, 0, 1) => Some(5.5), (2, 1, 1, 0, 1, 0) => Some(5.8), (2, 1, 1, 0, 1, 1) => Some(4.5), (2, 1, 1, 0, 2, 0) => Some(4.0), (2, 1, 1, 0, 2, 1) => Some(2.1), (2, 1, 1, 1, 0, 0) => Some(6.1), (2, 1, 1, 1, 0, 1) => Some(5.1), (2, 1, 1, 1, 1, 0) => Some(4.8), (2, 1, 1, 1, 1, 1) => Some(1.8), (2, 1, 1, 1, 2, 0) => Some(2.0), (2, 1, 1, 1, 2, 1) => Some(0.9), (2, 1, 1, 2, 0, 0) => Some(4.6), (2, 1, 1, 2, 0, 1) => Some(1.8), (2, 1, 1, 2, 1, 0) => Some(1.7), (2, 1, 1, 2, 1, 1) => Some(0.7), (2, 1, 1, 2, 2, 0) => Some(0.8), (2, 1, 1, 2, 2, 1) => Some(0.2), (2, 1, 2, 0, 0, 1) => Some(5.3), (2, 1, 2, 0, 1, 1) => Some(2.4), (2, 1, 2, 0, 2, 1) => Some(1.4), (2, 1, 2, 1, 0, 1) => Some(2.4), (2, 1, 2, 1, 1, 1) => Some(1.2), (2, 1, 2, 1, 2, 1) => Some(0.5), (2, 1, 2, 2, 0, 1) => Some(1.0), (2, 1, 2, 2, 1, 1) => Some(0.3), (2, 1, 2, 2, 2, 1) => Some(0.1), _ => None, } } cvss-2.2.0/src/v4/scoring/max_composed.rs000064400000000000000000000106351046102023000164200ustar 00000000000000use crate::v4::scoring::VectorEq; use alloc::vec::Vec; use std::vec; /// Highest severity vectors /// /// pub(super) fn max_composed(eq: VectorEq) -> Vec<&'static str> { match eq { VectorEq::Eq1(0) => vec!["AV:N/PR:N/UI:N/"], VectorEq::Eq1(1) => vec!["AV:A/PR:N/UI:N/", "AV:N/PR:L/UI:N/", "AV:N/PR:N/UI:P/"], VectorEq::Eq1(2) => vec!["AV:P/PR:N/UI:N/", "AV:A/PR:L/UI:P/"], VectorEq::Eq2(0) => vec!["AC:L/AT:N/"], VectorEq::Eq2(1) => vec!["AC:H/AT:N/", "AC:L/AT:P/"], VectorEq::Eq3Eq6(0, 0) => vec!["VC:H/VI:H/VA:H/CR:H/IR:H/AR:H/"], VectorEq::Eq3Eq6(0, 1) => vec![ "VC:H/VI:H/VA:L/CR:M/IR:M/AR:H/", "VC:H/VI:H/VA:H/CR:M/IR:M/AR:M/", ], VectorEq::Eq3Eq6(1, 0) => vec![ "VC:L/VI:H/VA:H/CR:H/IR:H/AR:H/", "VC:H/VI:L/VA:H/CR:H/IR:H/AR:H/", ], VectorEq::Eq3Eq6(1, 1) => vec![ "VC:L/VI:H/VA:L/CR:H/IR:M/AR:H/", "VC:L/VI:H/VA:H/CR:H/IR:M/AR:M/", "VC:H/VI:L/VA:H/CR:M/IR:H/AR:M/", "VC:H/VI:L/VA:L/CR:M/IR:H/AR:H/", "VC:L/VI:L/VA:H/CR:H/IR:H/AR:M/", ], VectorEq::Eq3Eq6(2, 1) => vec!["VC:L/VI:L/VA:L/CR:H/IR:H/AR:H/"], VectorEq::Eq4(0) => vec!["SC:H/SI:S/SA:S/"], VectorEq::Eq4(1) => vec!["SC:H/SI:H/SA:H/"], VectorEq::Eq4(2) => vec!["SC:L/SI:L/SA:L/"], VectorEq::Eq5(0) => vec!["E:A/"], VectorEq::Eq5(1) => vec!["E:P/"], VectorEq::Eq5(2) => vec!["E:U/"], _ => unreachable!("Unexpected vector: {:?}", eq), } } #[cfg(test)] mod tests { use super::*; use crate::v4::scoring::ScoringVector; use crate::v4::scoring::VectorEq::*; use std::format; #[test] fn all_combination_are_valid_scoring_vectors() { for eq1 in 0..3 { for eq2 in 0..2 { for eq3 in 0..3 { for eq4 in 0..3 { for eq5 in 0..3 { for eq6 in 0..2 { if eq3 == 2 && eq6 == 0 { // impossible combination continue; } let eq1_vals = max_composed(Eq1(eq1)); let eq2_vals = max_composed(Eq2(eq2)); let eq3_eq6_vals = max_composed(Eq3Eq6(eq3, eq6)); let eq4_vals = max_composed(Eq4(eq4)); let eq5_vals = max_composed(Eq5(eq5)); for eq1_val in eq1_vals.iter() { for eq2_val in eq2_vals.iter() { for eq3_eq6_val in eq3_eq6_vals.iter() { for eq4_val in eq4_vals.iter() { for eq5_val in eq5_vals.iter() { let scoring_vector = format!( "{}{}{}{}{}", eq1_val, eq2_val, eq3_eq6_val, eq4_val, eq5_val ); // ensure the conversion does not panic. assert!( ScoringVector::from_max_composed_vector( &scoring_vector ) .score() > 0.0 ); } } } } } } } } } } } } } cvss-2.2.0/src/v4/scoring/max_severity.rs000064400000000000000000000015421046102023000164560ustar 00000000000000use crate::v4::scoring::VectorEq; /// Max severity distances in EQs MacroVectors (+1) /// /// pub(super) fn max_severity(eq: VectorEq) -> u8 { match eq { VectorEq::Eq1(0) => 1, VectorEq::Eq1(1) => 4, VectorEq::Eq1(2) => 5, VectorEq::Eq2(0) => 1, VectorEq::Eq2(1) => 2, VectorEq::Eq3Eq6(0, 0) => 7, VectorEq::Eq3Eq6(0, 1) => 6, VectorEq::Eq3Eq6(1, 0) => 8, VectorEq::Eq3Eq6(1, 1) => 8, VectorEq::Eq3Eq6(2, 1) => 10, VectorEq::Eq4(0) => 6, VectorEq::Eq4(1) => 5, VectorEq::Eq4(2) => 4, VectorEq::Eq5(0) => 1, VectorEq::Eq5(1) => 1, VectorEq::Eq5(2) => 1, _ => unreachable!("Unexpected vector: {:?}", eq), } } cvss-2.2.0/src/v4/scoring.rs000064400000000000000000000523621046102023000137450ustar 00000000000000//! Score computation use crate::v4::{ MetricType, Vector, metric::{ MetricLevel, base::{ MergedAttackComplexity, MergedAttackRequirements, MergedAttackVector, MergedAvailabilityImpactToTheSubsequentSystem, MergedAvailabilityImpactToTheVulnerableSystem, MergedConfidentialityImpactToTheSubsequentSystem, MergedConfidentialityImpactToTheVulnerableSystem, MergedIntegrityImpactToTheSubsequentSystem, MergedIntegrityImpactToTheVulnerableSystem, MergedPrivilegesRequired, MergedUserInteraction, }, environmental::{ MergedAvailabilityRequirements, MergedConfidentialityRequirements, MergedIntegrityRequirements, }, supplemental::{ Automatable, ProviderUrgency, Recovery, Safety, ValueDensity, VulnerabilityResponseEffort, }, threat::MergedExploitMaturity, }, scoring::{lookup::lookup_global, max_composed::max_composed, max_severity::max_severity}, }; use alloc::vec::Vec; use std::{format, vec}; mod lookup; mod max_composed; mod max_severity; #[derive(Hash, Debug)] pub(crate) enum VectorEq { Eq1(u8), Eq2(u8), Eq3Eq6(u8, u8), Eq4(u8), Eq5(u8), } /// `ScoringVector` contains the data necessary for scoring, as it is transformed /// before the computations, and the types are subtly different. /// /// Contains the result of the `m()` function of the reference implementation. #[derive(Default)] pub(crate) struct ScoringVector { // Base scores overridden by modified scores ac: MergedAttackComplexity, at: MergedAttackRequirements, av: MergedAttackVector, pr: MergedPrivilegesRequired, sa: MergedAvailabilityImpactToTheSubsequentSystem, sc: MergedConfidentialityImpactToTheSubsequentSystem, si: MergedIntegrityImpactToTheSubsequentSystem, ui: MergedUserInteraction, va: MergedAvailabilityImpactToTheVulnerableSystem, vc: MergedConfidentialityImpactToTheVulnerableSystem, vi: MergedIntegrityImpactToTheVulnerableSystem, e: MergedExploitMaturity, ar: MergedAvailabilityRequirements, cr: MergedConfidentialityRequirements, ir: MergedIntegrityRequirements, au: Automatable, r: Recovery, re: VulnerabilityResponseEffort, s: Safety, u: ProviderUrgency, v: ValueDensity, } impl From<&Vector> for ScoringVector { fn from(v: &Vector) -> Self { Self { // All of these are unwrapped because they are mandatory // and the constructor ensures they are present. ac: v.ac.unwrap().merge(v.mac), at: v.at.unwrap().merge(v.mat), av: v.av.unwrap().merge(v.mav), pr: v.pr.unwrap().merge(v.mpr), sa: v.sa.unwrap().merge(v.msa), sc: v.sc.unwrap().merge(v.msc), si: v.si.unwrap().merge(v.msi), ui: v.ui.unwrap().merge(v.mui), va: v.va.unwrap().merge(v.mva), vc: v.vc.unwrap().merge(v.mvc), vi: v.vi.unwrap().merge(v.mvi), e: v.e.unwrap_or_default().merge(), ar: v.ar.unwrap_or_default().merge(), cr: v.cr.unwrap_or_default().merge(), ir: v.ir.unwrap_or_default().merge(), au: v.au.unwrap_or_default(), r: v.r.unwrap_or_default(), re: v.re.unwrap_or_default(), s: v.s.unwrap_or_default(), u: v.u.unwrap_or_default(), v: v.v.unwrap_or_default(), } } } impl ScoringVector { /// Read partial vectors from a string, to be used with `max_composed` /// output (as it contains `Merge` values, like `SI:S`). fn from_max_composed_vector(s: &str) -> Self { let s = s.trim_end_matches('/'); let components: Vec<(&str, &str)> = s .split('/') .map(|component| { let mut parts = component.split(':'); let id = parts.next().unwrap(); let value = parts.next().unwrap(); (id, value) }) .collect(); let mut metrics = Self { ..Default::default() }; for component in components { let id = component.0.to_ascii_uppercase(); let value = component.1.to_ascii_uppercase(); match id.parse::().unwrap() { MetricType::AV => metrics.av = value.parse().unwrap(), MetricType::AC => metrics.ac = value.parse().unwrap(), MetricType::PR => metrics.pr = value.parse().unwrap(), MetricType::UI => metrics.ui = value.parse().unwrap(), MetricType::S => metrics.s = value.parse().unwrap(), MetricType::AT => metrics.at = value.parse().unwrap(), MetricType::SA => metrics.sa = value.parse().unwrap(), MetricType::SC => metrics.sc = value.parse().unwrap(), MetricType::SI => metrics.si = value.parse().unwrap(), MetricType::VA => metrics.va = value.parse().unwrap(), MetricType::VC => metrics.vc = value.parse().unwrap(), MetricType::VI => metrics.vi = value.parse().unwrap(), MetricType::E => metrics.e = value.parse().unwrap(), MetricType::AR => metrics.ar = value.parse().unwrap(), MetricType::CR => metrics.cr = value.parse().unwrap(), MetricType::IR => metrics.ir = value.parse().unwrap(), MetricType::AU => metrics.au = value.parse().unwrap(), MetricType::R => metrics.r = value.parse().unwrap(), MetricType::RE => metrics.re = value.parse().unwrap(), MetricType::U => metrics.u = value.parse().unwrap(), MetricType::V => metrics.v = value.parse().unwrap(), _ => unreachable!("Invalid metric type in max_composed: {}", id), } } metrics } fn eq1(&self) -> u8 { // EQ1: 0- AV:N and PR:N and UI:N // 1- (AV:N or PR:N or UI:N) and not (AV:N and PR:N and UI:N) and not // AV:P 2- not(AV:N or PR:N or UI:N) or // AV:P if self.av == MergedAttackVector::Network && self.pr == MergedPrivilegesRequired::None && self.ui == MergedUserInteraction::None { 0 } else if (self.av == MergedAttackVector::Network || self.pr == MergedPrivilegesRequired::None || self.ui == MergedUserInteraction::None) && self.av != MergedAttackVector::Physical { 1 } else { 2 } } fn eq2(&self) -> u8 { // EQ2: 0- (AC:L and AT:N) // 1-(not(AC:L and AT:N)) if self.ac == MergedAttackComplexity::Low && self.at == MergedAttackRequirements::None { 0 } else { 1 } } fn eq3(&self) -> u8 { // EQ3: 0- (VC:H and VI:H) // 1-(not(VC:H and VI:H) and (VC:H or VI:H or VA:H)) // 2- not (VC:H or VI:H or VA:H) if self.vc == MergedConfidentialityImpactToTheVulnerableSystem::High && self.vi == MergedIntegrityImpactToTheVulnerableSystem::High { 0 } else if self.vc == MergedConfidentialityImpactToTheVulnerableSystem::High || self.vi == MergedIntegrityImpactToTheVulnerableSystem::High || self.va == MergedAvailabilityImpactToTheVulnerableSystem::High { 1 } else { 2 } } fn eq4(&self) -> u8 { // EQ4: 0- (MSI:S or MSA:S) // 1-not (MSI:S or MSA:S) and (SC:H or SI:H or SA:H) // 2-not (MSI:S or MSA:S) and not (SC:H or SI:H or SA:H) if self.si == MergedIntegrityImpactToTheSubsequentSystem::Safety || self.sa == MergedAvailabilityImpactToTheSubsequentSystem::Safety { 0 } else if self.sc == MergedConfidentialityImpactToTheSubsequentSystem::High || self.si == MergedIntegrityImpactToTheSubsequentSystem::High || self.sa == MergedAvailabilityImpactToTheSubsequentSystem::High { 1 } else { 2 } } fn eq5(&self) -> u8 { // EQ5: 0-E:A // 1-E:P // 2-E:U if self.e == MergedExploitMaturity::Attacked { 0 } else if self.e == MergedExploitMaturity::ProofOfConcept { 1 } else { 2 } } fn eq6(&self) -> u8 { // EQ6: 0- (CR:H and VC:H) or (IR:H and VI:H) or (AR:H and VA:H) // 1-not[(CR:H and VC:H) or (IR:H and VI:H) or (AR:H and VA:H)] if (self.cr == MergedConfidentialityRequirements::High && self.vc == MergedConfidentialityImpactToTheVulnerableSystem::High) || (self.ir == MergedIntegrityRequirements::High && self.vi == MergedIntegrityImpactToTheVulnerableSystem::High) || (self.ar == MergedAvailabilityRequirements::High && self.va == MergedAvailabilityImpactToTheVulnerableSystem::High) { 0 } else { 1 } } fn macro_vector(&self) -> MacroVector { MacroVector::new( self.eq1(), self.eq2(), self.eq3(), self.eq4(), self.eq5(), self.eq6(), ) } pub(crate) fn score(&self) -> f64 { // Exception for no impact on system (shortcut) if self.vc == MergedConfidentialityImpactToTheVulnerableSystem::None && self.vi == MergedIntegrityImpactToTheVulnerableSystem::None && self.va == MergedAvailabilityImpactToTheVulnerableSystem::None && self.sc == MergedConfidentialityImpactToTheSubsequentSystem::None && self.si == MergedIntegrityImpactToTheSubsequentSystem::None && self.sa == MergedAvailabilityImpactToTheSubsequentSystem::None { return 0.; } let macro_vector = self.macro_vector(); let value = lookup_global(¯o_vector).unwrap(); // 1. For each of the EQs: // a. The maximal scoring difference is determined as the difference // between the current MacroVector and the lower MacroVector. // i. If there is no lower MacroVector the available distance is // set to NaN and then ignored in the further calculations. // compute next lower macro, it can also not exist // get their score, if the next lower macro score do not exist the result is // None let score_eq1_next_lower_macro = lookup_global(¯o_vector.incr_eq1()); let score_eq2_next_lower_macro = lookup_global(¯o_vector.incr_eq2()); // eq3 and eq6 are related let score_eq3eq6_next_lower_macro = if macro_vector.eq3 == 1 && macro_vector.eq6 == 1 { // 11 --> 21 lookup_global(¯o_vector.incr_eq3()) } else if macro_vector.eq3 == 0 && macro_vector.eq6 == 1 { // 01 --> 11 lookup_global(¯o_vector.incr_eq3()) } else if macro_vector.eq3 == 1 && macro_vector.eq6 == 0 { // 10 --> 11 lookup_global(¯o_vector.incr_eq6()) } else if macro_vector.eq3 == 0 && macro_vector.eq6 == 0 { // 00 --> 01 // 00 --> 10 let score_eq3eq6_next_lower_macro_left = lookup_global(¯o_vector.incr_eq6()); let score_eq3eq6_next_lower_macro_right = lookup_global(¯o_vector.incr_eq3()); if score_eq3eq6_next_lower_macro_left > score_eq3eq6_next_lower_macro_right { score_eq3eq6_next_lower_macro_left } else { score_eq3eq6_next_lower_macro_right } } else { // 21 --> 32 (do not exist) lookup_global(¯o_vector.incr_eq3()) }; let score_eq4_next_lower_macro = lookup_global(¯o_vector.incr_eq4()); let score_eq5_next_lower_macro = lookup_global(¯o_vector.incr_eq5()); // b. The severity distance of the to-be scored vector from a // highest severity vector in the same MacroVector is determined. let eq1_maxes = max_composed(VectorEq::Eq1(macro_vector.eq1)); let eq2_maxes = max_composed(VectorEq::Eq2(macro_vector.eq2)); let eq3_eq6_maxes = max_composed(VectorEq::Eq3Eq6(macro_vector.eq3, macro_vector.eq6)); let eq4_maxes = max_composed(VectorEq::Eq4(macro_vector.eq4)); let eq5_maxes = max_composed(VectorEq::Eq5(macro_vector.eq5)); // compose them let mut max_vectors = vec![]; for eq1_max in &eq1_maxes { for eq2_max in &eq2_maxes { for eq3_eq6_max in &eq3_eq6_maxes { for eq4_max in &eq4_maxes { for eq5max in &eq5_maxes { max_vectors .push(format!("{eq1_max}{eq2_max}{eq3_eq6_max}{eq4_max}{eq5max}")) } } } } } let mut severity_distance_av = 0.; let mut severity_distance_pr = 0.; let mut severity_distance_ui = 0.; let mut severity_distance_ac = 0.; let mut severity_distance_at = 0.; let mut severity_distance_vc = 0.; let mut severity_distance_vi = 0.; let mut severity_distance_va = 0.; let mut severity_distance_sc = 0.; let mut severity_distance_si = 0.; let mut severity_distance_sa = 0.; let mut severity_distance_cr = 0.; let mut severity_distance_ir = 0.; let mut severity_distance_ar = 0.; // Find the max vector to use i.e. one in the combination of all the highest // that is greater or equal (severity distance) than the to-be scored vector. for max_vector in &max_vectors { let max_vector = max_vector.trim_end_matches('/'); let v = ScoringVector::from_max_composed_vector(max_vector); severity_distance_av = self.av.level() - v.av.level(); severity_distance_pr = self.pr.level() - v.pr.level(); severity_distance_ui = self.ui.level() - v.ui.level(); severity_distance_ac = self.ac.level() - v.ac.level(); severity_distance_at = self.at.level() - v.at.level(); severity_distance_vc = self.vc.level() - v.vc.level(); severity_distance_vi = self.vi.level() - v.vi.level(); severity_distance_va = self.va.level() - v.va.level(); severity_distance_sc = self.sc.level() - v.sc.level(); severity_distance_si = self.si.level() - v.si.level(); severity_distance_sa = self.sa.level() - v.sa.level(); severity_distance_cr = self.cr.level() - v.cr.level(); severity_distance_ir = self.ir.level() - v.ir.level(); severity_distance_ar = self.ar.level() - v.ar.level(); // if any is less than zero this is not the right max if severity_distance_av < 0. || severity_distance_pr < 0. || severity_distance_ui < 0. || severity_distance_ac < 0. || severity_distance_at < 0. || severity_distance_vc < 0. || severity_distance_vi < 0. || severity_distance_va < 0. || severity_distance_sc < 0. || severity_distance_si < 0. || severity_distance_sa < 0. || severity_distance_cr < 0. || severity_distance_ir < 0. || severity_distance_ar < 0. { continue; } else { // if multiple maxes exist to reach it it is enough the first one break; } } let current_severity_distance_eq1 = severity_distance_av + severity_distance_pr + severity_distance_ui; let current_severity_distance_eq2 = severity_distance_ac + severity_distance_at; let current_severity_distance_eq3eq6 = severity_distance_vc + severity_distance_vi + severity_distance_va + severity_distance_cr + severity_distance_ir + severity_distance_ar; let current_severity_distance_eq4 = severity_distance_sc + severity_distance_si + severity_distance_sa; let step = 0.1; // if the next lower macro score does not exist the result is None let available_distance_eq1 = score_eq1_next_lower_macro.map(|v| value - v); let available_distance_eq2 = score_eq2_next_lower_macro.map(|v| value - v); let available_distance_eq3eq6 = score_eq3eq6_next_lower_macro.map(|v| value - v); let available_distance_eq4 = score_eq4_next_lower_macro.map(|v| value - v); let available_distance_eq5 = score_eq5_next_lower_macro.map(|v| value - v); // some of them do not exist, we will find them by retrieving the score. If // score null then do not exist. let mut n_existing_lower = 0; // multiply by step because distance is pure let max_severity_eq1 = max_severity(VectorEq::Eq1(macro_vector.eq1)) as f64 * step; let max_severity_eq2 = max_severity(VectorEq::Eq2(macro_vector.eq2)) as f64 * step; let max_severity_eq3eq6 = max_severity(VectorEq::Eq3Eq6(macro_vector.eq3, macro_vector.eq6)) as f64 * step; let max_severity_eq4 = max_severity(VectorEq::Eq4(macro_vector.eq4)) as f64 * step; // c. The proportion of the distance is determined by dividing // the severity distance of the to-be-scored vector by the depth // of the MacroVector. // d. The maximal scoring difference is multiplied by the proportion of // distance. let normalized_severity_eq1 = if let Some(a) = available_distance_eq1 { n_existing_lower += 1; let percent_to_next_eq1_severity = (current_severity_distance_eq1) / max_severity_eq1; a * percent_to_next_eq1_severity } else { 0. }; let normalized_severity_eq2 = if let Some(a) = available_distance_eq2 { n_existing_lower += 1; let percent_to_next_eq2_severity = (current_severity_distance_eq2) / max_severity_eq2; a * percent_to_next_eq2_severity } else { 0. }; let normalized_severity_eq3eq6 = if let Some(a) = available_distance_eq3eq6 { n_existing_lower += 1; let percent_to_next_eq3eq6_severity = (current_severity_distance_eq3eq6) / max_severity_eq3eq6; a * percent_to_next_eq3eq6_severity } else { 0. }; let normalized_severity_eq4 = if let Some(a) = available_distance_eq4 { n_existing_lower += 1; let percent_to_next_eq4_severity = (current_severity_distance_eq4) / max_severity_eq4; a * percent_to_next_eq4_severity } else { 0. }; let normalized_severity_eq5 = if let Some(a) = available_distance_eq5 { // for eq5 is always 0 the percentage n_existing_lower += 1; let percent_to_next_eq5_severity = 0.; a * percent_to_next_eq5_severity } else { 0. }; // 2. The mean of the above computed proportional distances is computed. let mean_distance = if n_existing_lower == 0 { 0. } else { // sometimes we need to go up but there is nothing there, or down but there is // nothing there so it's a change of 0. (normalized_severity_eq1 + normalized_severity_eq2 + normalized_severity_eq3eq6 + normalized_severity_eq4 + normalized_severity_eq5) / n_existing_lower as f64 }; // 3. The score of the vector is the score of the MacroVector (i.e. the score of // the highest severity vector) minus the mean distance so computed. This // score is rounded to one decimal place. value - mean_distance } } #[derive(Clone, Hash, PartialEq, Eq, Debug)] struct MacroVector { eq1: u8, eq2: u8, eq3: u8, eq4: u8, eq5: u8, eq6: u8, } impl MacroVector { fn new(eq1: u8, eq2: u8, eq3: u8, eq4: u8, eq5: u8, eq6: u8) -> Self { MacroVector { eq1, eq2, eq3, eq4, eq5, eq6, } } fn as_tuple(&self) -> (u8, u8, u8, u8, u8, u8) { (self.eq1, self.eq2, self.eq3, self.eq4, self.eq5, self.eq6) } fn incr_eq1(&self) -> Self { Self { eq1: self.eq1 + 1, ..*self } } fn incr_eq2(&self) -> Self { Self { eq2: self.eq2 + 1, ..*self } } fn incr_eq3(&self) -> Self { Self { eq3: self.eq3 + 1, ..*self } } fn incr_eq4(&self) -> Self { Self { eq4: self.eq4 + 1, ..*self } } fn incr_eq5(&self) -> Self { Self { eq5: self.eq5 + 1, ..*self } } fn incr_eq6(&self) -> Self { Self { eq6: self.eq6 + 1, ..*self } } } cvss-2.2.0/src/v4/vector.rs000064400000000000000000000453151046102023000136030ustar 00000000000000//! Score computing for CVSSv4 use crate::{ Error, PREFIX, v4::{ MetricType, metric::{ base::{ AttackComplexity, AttackRequirements, AttackVector, AvailabilityImpactToTheSubsequentSystem, AvailabilityImpactToTheVulnerableSystem, ConfidentialityImpactToTheSubsequentSystem, ConfidentialityImpactToTheVulnerableSystem, IntegrityImpactToTheSubsequentSystem, IntegrityImpactToTheVulnerableSystem, PrivilegesRequired, UserInteraction, }, environmental::{ AvailabilityRequirements, ConfidentialityRequirements, IntegrityRequirements, ModifiedAttackComplexity, ModifiedAttackRequirements, ModifiedAttackVector, ModifiedAvailabilityImpactToTheSubsequentSystem, ModifiedAvailabilityImpactToTheVulnerableSystem, ModifiedConfidentialityImpactToTheSubsequentSystem, ModifiedConfidentialityImpactToTheVulnerableSystem, ModifiedIntegrityImpactToTheSubsequentSystem, ModifiedIntegrityImpactToTheVulnerableSystem, ModifiedPrivilegesRequired, ModifiedUserInteraction, }, supplemental::{ Automatable, ProviderUrgency, Recovery, Safety, ValueDensity, VulnerabilityResponseEffort, }, threat::ExploitMaturity, }, }, }; use alloc::{borrow::ToOwned, string::String, vec::Vec}; use core::{fmt, str::FromStr}; #[cfg(feature = "serde")] use { alloc::string::ToString, serde::{Deserialize, Serialize, de, ser}, }; #[cfg(feature = "std")] use crate::v4::Score; use crate::v4::score::Nomenclature; /// A CVSS 4.0 vector #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct Vector { /// Minor component of the version pub minor_version: usize, /// Attack Complexity (AC) pub(crate) ac: Option, /// Attack Requirements (AT) pub(crate) at: Option, /// Attack Vector (AV) pub(crate) av: Option, /// Privileges Required (PR) pub(crate) pr: Option, /// Availability Impact to the Subsequent System (SA) pub(crate) sa: Option, /// Confidentiality Impact to the Subsequent System (SC) pub(crate) sc: Option, /// Integrity Impact to the Subsequent System (SI) pub(crate) si: Option, /// User Interaction (UI) pub(crate) ui: Option, /// Availability Impact to the Vulnerable System (VA) pub(crate) va: Option, /// Confidentiality Impact to the Vulnerable System (VC) pub(crate) vc: Option, /// Integrity Impact to the Vulnerable System (VI) pub(crate) vi: Option, /// Exploit Maturity (E) pub(crate) e: Option, /// Availability Requirements (AR) pub(crate) ar: Option, /// Confidentiality Requirements (CR) pub(crate) cr: Option, /// Integrity Requirements (IR) pub(crate) ir: Option, /// Modified Attack Complexity (AC) pub(crate) mac: Option, /// Modified Attack Requirements (MAT) pub(crate) mat: Option, /// Modified Attack Vector (MAV) pub(crate) mav: Option, /// Modified Privileges Required (MPR) pub(crate) mpr: Option, /// Modified Availability Impact to the Subsequent System (MSA) pub(crate) msa: Option, /// Modified Confidentiality Impact to the Subsequent System (MSC) pub(crate) msc: Option, /// Modified Integrity Impact to the Subsequent System (MSI) pub(crate) msi: Option, /// Modified User Interaction (MUI) pub(crate) mui: Option, /// Modified Availability Impact to the Vulnerable System (MVA) pub(crate) mva: Option, /// Modified Confidentiality Impact to the Vulnerable System (MVC) pub(crate) mvc: Option, /// Modified Integrity Impact to the Vulnerable System (MVI) pub(crate) mvi: Option, /// Automatable (AU) pub(crate) au: Option, /// Recovery (R) pub(crate) r: Option, /// Vulnerability Response Effort (RE) pub(crate) re: Option, /// Safety (S) pub(crate) s: Option, /// Provider Urgency (U) pub(crate) u: Option, /// Value Density (V) pub(crate) v: Option, } impl Vector { /// Get the numerical score of the vector #[cfg(feature = "std")] pub fn score(&self) -> Score { self.into() } /// Get the nomenclature of the vector /// /// This nomenclature should be used wherever a numerical CVSS value is displayed or communicated. pub fn nomenclature(&self) -> Nomenclature { Nomenclature::from(self) } /// Iterate over all defined vector metrics pub fn metrics(&self) -> impl Iterator { [ ( MetricType::AC, self.ac.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::AT, self.at.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::AV, self.av.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::PR, self.pr.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::SA, self.sa.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::SC, self.sc.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::SI, self.si.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::UI, self.ui.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::VA, self.va.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::VC, self.vc.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::VI, self.vi.as_ref().map(|m| m as &dyn fmt::Debug), ), (MetricType::E, self.e.as_ref().map(|m| m as &dyn fmt::Debug)), ( MetricType::AR, self.ar.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::CR, self.cr.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::IR, self.ir.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::MAC, self.mac.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::MAT, self.mat.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::MAV, self.mav.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::MPR, self.mpr.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::MSA, self.msa.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::MSC, self.msc.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::MSI, self.msi.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::MUI, self.mui.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::MVA, self.mva.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::MVC, self.mvc.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::MVI, self.mvi.as_ref().map(|m| m as &dyn fmt::Debug), ), ( MetricType::AU, self.au.as_ref().map(|m| m as &dyn fmt::Debug), ), (MetricType::R, self.r.as_ref().map(|m| m as &dyn fmt::Debug)), ( MetricType::RE, self.re.as_ref().map(|m| m as &dyn fmt::Debug), ), (MetricType::S, self.s.as_ref().map(|m| m as &dyn fmt::Debug)), (MetricType::U, self.u.as_ref().map(|m| m as &dyn fmt::Debug)), (MetricType::V, self.v.as_ref().map(|m| m as &dyn fmt::Debug)), ] .into_iter() .filter_map(|(ty, m)| m.map(|m| (ty, m))) } /// Check for required base metrics presence /// /// Defined in fn check_mandatory_metrics(&self) -> Result<(), Error> { fn ensure_present(metric: Option, metric_type: MetricType) -> Result<(), Error> { if metric.is_none() { return Err(Error::MissingMandatoryMetricV4 { metric_type }); } Ok(()) } ensure_present(self.ac.as_ref(), MetricType::AC)?; ensure_present(self.at.as_ref(), MetricType::AT)?; ensure_present(self.av.as_ref(), MetricType::AV)?; ensure_present(self.pr.as_ref(), MetricType::PR)?; ensure_present(self.sa.as_ref(), MetricType::SA)?; ensure_present(self.sc.as_ref(), MetricType::SC)?; ensure_present(self.si.as_ref(), MetricType::SI)?; ensure_present(self.ui.as_ref(), MetricType::UI)?; ensure_present(self.va.as_ref(), MetricType::VA)?; ensure_present(self.vc.as_ref(), MetricType::VC)?; ensure_present(self.vi.as_ref(), MetricType::VI)?; Ok(()) } } macro_rules! write_metrics { ($f:expr, $($metric:expr),+) => { $( if let Some(metric) = $metric { write!($f, "/{}", metric)?; } )+ }; } impl fmt::Display for Vector { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:4.{}", PREFIX, self.minor_version)?; write_metrics!( f, self.av, self.ac, self.at, self.pr, self.ui, self.vc, self.vi, self.va, self.sc, self.si, self.sa, self.e, self.cr, self.ir, self.ar, self.mav, self.mac, self.mat, self.mpr, self.mui, self.mvc, self.mvi, self.mva, self.msc, self.msi, self.msa, self.s, self.au, self.r, self.v, self.re, self.u ); Ok(()) } } impl FromStr for Vector { type Err = Error; fn from_str(s: &str) -> crate::Result { let component_vec = s .split('/') .map(|component| { let mut parts = component.split(':'); let id = parts.next().ok_or_else(|| Error::InvalidComponent { component: component.to_owned(), })?; let value = parts.next().ok_or_else(|| Error::InvalidComponent { component: component.to_owned(), })?; if parts.next().is_some() { return Err(Error::InvalidComponent { component: component.to_owned(), }); } Ok((id, value)) }) .collect::>>()?; let mut components = component_vec.iter(); let &(id, version_string) = components.next().ok_or(Error::InvalidPrefix { prefix: s.to_owned(), })?; if id != PREFIX { return Err(Error::InvalidPrefix { prefix: id.to_owned(), }); } let mut metrics = Self { minor_version: match version_string { "4.0" => 0, _ => { return Err(Error::UnsupportedVersion { version: version_string.to_owned(), }); } }, ..Default::default() }; for &component in components { let id = component.0.to_ascii_uppercase(); let value = component.1.to_ascii_uppercase(); fn get_value>( metric_type: MetricType, current_val: Option, new_val: String, ) -> Result, Error> { let parsed: T = new_val.parse()?; if current_val.is_some() { return Err(Error::DuplicateMetricV4 { metric_type }); } Ok(Some(parsed)) } match id.parse::()? { MetricType::AV => metrics.av = get_value(MetricType::AV, metrics.av, value)?, MetricType::AC => metrics.ac = get_value(MetricType::AC, metrics.ac, value)?, MetricType::PR => metrics.pr = get_value(MetricType::PR, metrics.pr, value)?, MetricType::UI => metrics.ui = get_value(MetricType::UI, metrics.ui, value)?, MetricType::S => metrics.s = get_value(MetricType::S, metrics.s, value)?, MetricType::AT => metrics.at = get_value(MetricType::AT, metrics.at, value)?, MetricType::SA => metrics.sa = get_value(MetricType::SA, metrics.sa, value)?, MetricType::SC => metrics.sc = get_value(MetricType::SC, metrics.sc, value)?, MetricType::SI => metrics.si = get_value(MetricType::SI, metrics.si, value)?, MetricType::VA => metrics.va = get_value(MetricType::VA, metrics.va, value)?, MetricType::VC => metrics.vc = get_value(MetricType::VC, metrics.vc, value)?, MetricType::VI => metrics.vi = get_value(MetricType::VI, metrics.vi, value)?, MetricType::E => metrics.e = get_value(MetricType::E, metrics.e, value)?, MetricType::AR => metrics.ar = get_value(MetricType::AR, metrics.ar, value)?, MetricType::CR => metrics.cr = get_value(MetricType::CR, metrics.cr, value)?, MetricType::IR => metrics.ir = get_value(MetricType::IR, metrics.ir, value)?, MetricType::MAC => metrics.mac = get_value(MetricType::MAC, metrics.mac, value)?, MetricType::MAT => metrics.mat = get_value(MetricType::MAT, metrics.mat, value)?, MetricType::MAV => metrics.mav = get_value(MetricType::MAV, metrics.mav, value)?, MetricType::MPR => metrics.mpr = get_value(MetricType::MPR, metrics.mpr, value)?, MetricType::MSA => metrics.msa = get_value(MetricType::MSA, metrics.msa, value)?, MetricType::MSC => metrics.msc = get_value(MetricType::MSC, metrics.msc, value)?, MetricType::MSI => metrics.msi = get_value(MetricType::MSI, metrics.msi, value)?, MetricType::MUI => metrics.mui = get_value(MetricType::MUI, metrics.mui, value)?, MetricType::MVA => metrics.mva = get_value(MetricType::MVA, metrics.mva, value)?, MetricType::MVC => metrics.mvc = get_value(MetricType::MVC, metrics.mvc, value)?, MetricType::MVI => metrics.mvi = get_value(MetricType::MVI, metrics.mvi, value)?, MetricType::AU => metrics.au = get_value(MetricType::AU, metrics.au, value)?, MetricType::R => metrics.r = get_value(MetricType::R, metrics.r, value)?, MetricType::RE => metrics.re = get_value(MetricType::RE, metrics.re, value)?, MetricType::U => metrics.u = get_value(MetricType::U, metrics.u, value)?, MetricType::V => metrics.v = get_value(MetricType::V, metrics.v, value)?, } } metrics.check_mandatory_metrics()?; Ok(metrics) } } #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl<'de> Deserialize<'de> for Vector { fn deserialize>(deserializer: D) -> Result { String::deserialize(deserializer)? .parse() .map_err(de::Error::custom) } } #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] impl Serialize for Vector { fn serialize(&self, serializer: S) -> Result { self.to_string().serialize(serializer) } } #[cfg(test)] #[cfg(feature = "std")] mod tests { use super::*; use alloc::{borrow::ToOwned, string::ToString}; #[test] fn fails_to_parse_invalid_cvss4() { // Version 5.0 is not supported assert_eq!( Vector::from_str("CVSS:5.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N"), Err(Error::UnsupportedVersion { version: "5.0".to_string(), }) ); // Invalid prefix CSS assert_eq!( Vector::from_str("CSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N"), Err(Error::InvalidPrefix { prefix: "CSS".to_owned(), }) ); // “F” is not a valid value for “AV” assert_eq!( Vector::from_str("CVSS:4.0/AV:F/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N"), Err(Error::InvalidMetricV4 { metric_type: MetricType::AV, value: "F".to_owned() }) ); // Missing mandatory metric “AC” assert_eq!( Vector::from_str("CVSS:4.0/AV:N/AT:N/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N"), Err(Error::MissingMandatoryMetricV4 { metric_type: MetricType::AC }) ); } #[test] fn parse_base_cvss4() { assert!( Vector::from_str("CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N") .is_ok() ); } #[test] fn parse_full_cvss4() { let vector_s = "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N/E:U/CR:L/IR:X/AR:L/MAV:A/MAC:H/MAT:N/MPR:N/MUI:P/MVC:X/MVI:N/MVA:H/MSC:N/MSI:L/MSA:S/S:N/AU:N/R:I/V:C/RE:H/U:Green"; let v = Vector::from_str(vector_s).unwrap(); assert_eq!(vector_s, v.to_string()); } } cvss-2.2.0/src/v4.rs000064400000000000000000000004431046102023000122720ustar 00000000000000//! Common Vulnerability Scoring System (v4.0) //! //! pub mod metric; pub mod score; #[cfg(feature = "std")] mod scoring; mod vector; pub use self::{ metric::MetricType, score::{Nomenclature, Score}, vector::Vector, }; cvss-2.2.0/tests/base.rs000064400000000000000000000214211046102023000132250ustar 00000000000000#![cfg(all(feature = "v3", feature = "std"))] /// Base Metrics tests /// /// NOTE: These CVEs are actually `CVSS:3.0` and have been modified for the /// purposes of these tests use core::str::FromStr; /// CVE-2013-1937 #[test] fn cve_2013_1937() { let cvss_for_cve_2013_1937 = "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"; let base = cvss::v3::Base::from_str(cvss_for_cve_2013_1937).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2013_1937); assert_eq!(base.score().value(), 6.1); } /// Missing CVSS:3.1 prefix #[test] fn bad_prefix() { let cvss_for_cve_2013_1937e = "AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"; assert!(cvss::v3::Base::from_str(cvss_for_cve_2013_1937e).is_err()); } /// CVSS:3.0 prefix (parse these as for the purposes of this library they're identical) #[test] fn cvss_v3_0_prefix() { let cvss_for_cve_2013_1937 = "CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"; let base = cvss::v3::Base::from_str(cvss_for_cve_2013_1937).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2013_1937); assert_eq!(base.score().value(), 6.1); } /// CVE-2013-0375 #[test] fn cve_2013_0375() { let cvss_for_cve_2013_0375 = "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N"; let base = cvss::v3::Base::from_str(cvss_for_cve_2013_0375).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2013_0375); assert_eq!(base.score().value(), 6.4); } /// CVE-2014-3566 #[test] fn cve_2014_3566() { let cvss_for_cve_2014_3566 = "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N"; let base = cvss::v3::Base::from_str(cvss_for_cve_2014_3566).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2014_3566); assert_eq!(base.score().value(), 3.1); } /// CVE-2012-1516 #[test] fn cve_2012_1516() { let cvss_for_cve_2012_1516 = "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H"; let base = cvss::v3::Base::from_str(cvss_for_cve_2012_1516).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2012_1516); assert_eq!(base.score().value(), 9.9); } /// CVE-2009-0783 #[test] fn cve_2009_0783() { let cvss_for_cve_2009_0783 = "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:L"; let base = cvss::v3::Base::from_str(cvss_for_cve_2009_0783).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2009_0783); assert_eq!(base.score().value(), 4.2); } /// CVE-2012-0384 #[test] fn cve_2012_0384() { let cvss_for_cve_2012_0384 = "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"; let base = cvss::v3::Base::from_str(cvss_for_cve_2012_0384).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2012_0384); assert_eq!(base.score().value(), 8.8); } /// CVE-2015-1098 #[test] fn cve_2015_1098() { let cvss_for_cve_2015_1098 = "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"; let base = cvss::v3::Base::from_str(cvss_for_cve_2015_1098).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2015_1098); assert_eq!(base.score().value(), 7.8); } /// CVE-2014-0160 #[test] fn cve_2014_0160() { let cvss_for_cve_2014_0160 = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"; let base = cvss::v3::Base::from_str(cvss_for_cve_2014_0160).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2014_0160); assert_eq!(base.score().value(), 7.5); } /// CVE-2014-6271 #[test] fn cve_2014_6271() { let cvss_for_cve_2014_6271 = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"; let base = cvss::v3::Base::from_str(cvss_for_cve_2014_6271).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2014_6271); assert_eq!(base.score().value(), 9.8); } /// CVE-2008-1447 #[test] fn cve_2008_1447() { let cvss_for_cve_2008_1447 = "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:N"; let base = cvss::v3::Base::from_str(cvss_for_cve_2008_1447).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2008_1447); assert_eq!(base.score().value(), 6.8); } /// CVE-2014-2005 #[test] fn cve_2014_2005() { let cvss_for_cve_2014_2005 = "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"; let base = cvss::v3::Base::from_str(cvss_for_cve_2014_2005).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2014_2005); assert_eq!(base.score().value(), 6.8); } /// CVE-2010-0467 #[test] fn cve_2010_0467() { let cvss_for_cve_2010_0467 = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N"; let base = cvss::v3::Base::from_str(cvss_for_cve_2010_0467).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2010_0467); assert_eq!(base.score().value(), 5.8); } /// CVE-2012-1342 #[test] fn cve_2012_1342() { let cvss_for_cve_2012_1342 = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:N"; let base = cvss::v3::Base::from_str(cvss_for_cve_2012_1342).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2012_1342); assert_eq!(base.score().value(), 5.8); } /// CVE-2013-6014 #[test] fn cve_2013_6014() { let cvss_for_cve_2013_6014 = "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:H"; let base = cvss::v3::Base::from_str(cvss_for_cve_2013_6014).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2013_6014); assert_eq!(base.score().value(), 9.3); } /// CVE-2014-9253 #[test] fn cve_2014_9253() { let cvss_for_cve_2014_9253 = "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N"; let base = cvss::v3::Base::from_str(cvss_for_cve_2014_9253).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2014_9253); assert_eq!(base.score().value(), 5.4); } /// CVE-2009-0658 #[test] fn cve_2009_0658() { let cvss_for_cve_2009_0658 = "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"; let base = cvss::v3::Base::from_str(cvss_for_cve_2009_0658).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2009_0658); assert_eq!(base.score().value(), 7.8); } /// CVE-2011-1265 #[test] fn cve_2011_1265() { let cvss_for_cve_2011_1265 = "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"; let base = cvss::v3::Base::from_str(cvss_for_cve_2011_1265).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2011_1265); assert_eq!(base.score().value(), 8.8); } /// CVE-2014-2019 #[test] fn cve_2014_2019() { let cvss_for_cve_2014_2019 = "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N"; let base = cvss::v3::Base::from_str(cvss_for_cve_2014_2019).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2014_2019); assert_eq!(base.score().value(), 4.6); } /// CVE-2015-0970 #[test] fn cve_2015_0970() { let cvss_for_cve_2015_0970 = "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"; let base = cvss::v3::Base::from_str(cvss_for_cve_2015_0970).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2015_0970); assert_eq!(base.score().value(), 8.8); } /// CVE-2014-0224 #[test] fn cve_2014_0224() { let cvss_for_cve_2014_0224 = "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N"; let base = cvss::v3::Base::from_str(cvss_for_cve_2014_0224).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2014_0224); assert_eq!(base.score().value(), 7.4); } /// CVE-2012-5376 #[test] fn cve_2012_5376() { let cvss_for_cve_2012_5376 = "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H"; let base = cvss::v3::Base::from_str(cvss_for_cve_2012_5376).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2012_5376); assert_eq!(base.score().value(), 9.6); } /// No impact scope changed #[test] fn no_impact_scope_changed() { // https://www.first.org/cvss/calculator/3.1#CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:N => 0.0 let cvss_for_no_impact_scope_changed = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:N"; let base = cvss::v3::Base::from_str(cvss_for_no_impact_scope_changed).unwrap(); assert_eq!(&base.to_string(), cvss_for_no_impact_scope_changed); assert_eq!(base.score().value(), 0.0); } /// No impact scope unchanged #[test] fn no_impact_scope_unchanged() { // https://www.first.org/cvss/calculator/3.1#CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N => 0.0 let cvss_for_no_impact_scope_unchanged = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N"; let base = cvss::v3::Base::from_str(cvss_for_no_impact_scope_unchanged).unwrap(); assert_eq!(&base.to_string(), cvss_for_no_impact_scope_unchanged); assert_eq!(base.score().value(), 0.0); } #[test] fn low_scope_unchanged() { // https://www.first.org/cvss/calculator/3.1#CVSS:3.1/AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:L => 1.6 let cvss_for_low_scope_unchanged = "CVSS:3.1/AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:L"; let base = cvss::v3::Base::from_str(cvss_for_low_scope_unchanged).unwrap(); assert_eq!(&base.to_string(), cvss_for_low_scope_unchanged); assert_eq!(base.score().value(), 1.6); } #[test] fn low_scope_changed() { // https://www.first.org/cvss/calculator/3.1#CVSS:3.1/AV:P/AC:H/PR:H/UI:R/S:C/C:N/I:N/A:L => 1.8 let cvss_for_low_scope_changed = "CVSS:3.1/AV:P/AC:H/PR:H/UI:R/S:C/C:N/I:N/A:L"; let base = cvss::v3::Base::from_str(cvss_for_low_scope_changed).unwrap(); assert_eq!(&base.to_string(), cvss_for_low_scope_changed); assert_eq!(base.score().value(), 1.8); } cvss-2.2.0/tests/redhat-v3.rs000064400000000000000000000023641046102023000141150ustar 00000000000000#![cfg(all(feature = "v3", feature = "std"))] use cvss::v3::Base; use std::{fs, str::FromStr}; // Run the test set from Red Hat's Security Python implementation: https://github.com/RedHatProductSecurity/cvss fn run_tests_from_file(name: &str) { let content = fs::read_to_string(format!("tests/cvss-redhat/tests/{}", name)).unwrap(); for l in content.lines() { let parts = l.split(" - ").collect::>(); let vector = parts[0]; if vector.len() > 44 { // more than base, skip continue; } // "(base, _, _)" let score = parts[1].split(',').next().unwrap().trim_start_matches('('); let cvss = Base::from_str(vector).unwrap(); // Test correct serialization. assert_eq!(cvss.to_string(), parts[0]); assert!(cvss.score().value() >= 0.0); assert!(cvss.score().value() <= 10.0); let diff: f64 = cvss.score().value() - score.parse::().unwrap(); assert!(diff.abs() < 0.0001); } } #[test] fn cvss_v3_simple() { run_tests_from_file("vectors_simple3"); run_tests_from_file("vectors_simple31"); } #[test] fn cvss_v3_random() { run_tests_from_file("vectors_random3"); run_tests_from_file("vectors_random31"); } cvss-2.2.0/tests/redhat-v4.rs000064400000000000000000000035271046102023000141200ustar 00000000000000#![cfg(all(feature = "v4", feature = "std"))] use cvss::v4::Vector; use std::{fs, str::FromStr}; // We run the test set from Red Hat's Security Python implementation: https://github.com/RedHatProductSecurity/cvss // It seems to be the best test set available (at least for CVSS v4.0). fn run_tests_from_file(name: &str, test_serialization: bool) { let content = fs::read_to_string(format!("tests/cvss-redhat/tests/{}", name)).unwrap(); for l in content.lines() { let parts = l.split(" - ").collect::>(); let cvss = Vector::from_str(parts[0]).unwrap(); if test_serialization { // Test correct serialization. assert_eq!(cvss.to_string(), parts[0]); } assert!(cvss.score().value() >= 0.0); assert!(cvss.score().value() <= 10.0); let diff: f64 = cvss.score().value() - parts[1].parse::().unwrap(); assert!(diff.abs() < 0.0001); } } #[test] fn cvss_v4_base() { // All vector combinations with only mandatory fields, 104,976 vectors. run_tests_from_file("vectors_base4", true); } #[test] fn cvss_v4_modified() { // All vector combinations of modified environmental fields, 373,248 vectors. run_tests_from_file("vectors_modified4", true); } #[test] fn cvss_v4_supplemental() { // All vector combinations of supplemental fields, 576 vectors. run_tests_from_file("vectors_supplemental4", true); } #[test] fn cvss_v4_security() { // All vector combinations of security fields, 54 vectors. run_tests_from_file("vectors_security4", true); } #[test] fn cvss_v4_threat() { // All vector combinations of threat fields, 6 vectors. run_tests_from_file("vectors_threat4", true); } #[test] fn cvss_v4_random() { // Random vector combinations across all fields, 10,000 vectors. run_tests_from_file("vectors_random4", false); } cvss-2.2.0/tests/uncovered-v4.rs000064400000000000000000000013301046102023000146310ustar 00000000000000//! Regression Tests //! //! NOTE: These CVEs are actually `CVSS:4.0` and have been picked from real //! practical cases not covered by Red Hat #![cfg(all(feature = "v4", feature = "std"))] use {cvss::v4::Vector, std::str::FromStr}; /// CVE-2025-47273 #[test] fn cve_2025_47273() { let cvss_for_cve_2025_47273 = concat!( "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N", "/E:P/CR:X/IR:X/AR:X", "/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X", "/S:X/AU:X/R:X/V:X/RE:X/U:X", ); let base = Vector::from_str(cvss_for_cve_2025_47273).unwrap(); assert_eq!(&base.to_string(), cvss_for_cve_2025_47273); assert_eq!(base.score().value(), 7.7); }