diff --git a/examples/get_dependabot_alerts.rs b/examples/get_dependabot_alerts.rs new file mode 100644 index 00000000..7b3102b7 --- /dev/null +++ b/examples/get_dependabot_alerts.rs @@ -0,0 +1,69 @@ +use http::header::ACCEPT; +use octocrab::models::repos::dependabot::UpdateDependabotAlert; +use octocrab::Octocrab; + +const OWNER: &str = "org"; +const REPO: &str = "some-repo"; + +#[tokio::main] +async fn main() { + // example for Dependabot alerts API with OAuth GitHub App + let client_id = secrecy::Secret::from(std::env::var("GITHUB_CLIENT_ID").unwrap()); + let crab = octocrab::Octocrab::builder() + .base_uri("https://github.com") + .unwrap() + .add_header(ACCEPT, "application/json".to_string()) + .build() + .unwrap(); + + let codes = crab + .authenticate_as_device(&client_id, ["security_events"]) + .await + .unwrap(); + println!( + "Go to {} and enter code {}", + codes.verification_uri, codes.user_code + ); + let auth = codes.poll_until_available(&crab, &client_id).await.unwrap(); + println!( + "Auth: scope {:?}; token type {}", + auth.scope, auth.token_type + ); + let octocrab = Octocrab::builder() + .oauth(auth) + .add_header(ACCEPT, "application/vnd.github+json".to_string()) + .build() + .unwrap(); + // Get all Dependabot alerts for a repo + let a = octocrab + .repos(OWNER, REPO) + .dependabot() + .direction("asc") + .get_alerts() + .await + .unwrap(); + println!("{:?}", a); + // Get a single Dependabot alert + let single_alert = octocrab + .repos(OWNER, REPO) + .dependabot() + .get_alert(5) + .await + .unwrap(); + println!("{:?}", single_alert); + // Update (dismiss) a Dependabot alert + let updated_alert = octocrab + .repos(OWNER, REPO) + .dependabot() + .update_alert( + 5, + Some(&UpdateDependabotAlert { + state: "dismissed", + dismissed_reason: Some("no_bandwidth"), + dismissed_comment: Some("I don't have time to fix this right now"), + }), + ) + .await + .unwrap(); + println!("{:?}", updated_alert); +} diff --git a/src/api/repos.rs b/src/api/repos.rs index 80c1f41d..7ba50667 100644 --- a/src/api/repos.rs +++ b/src/api/repos.rs @@ -11,6 +11,7 @@ mod branches; mod collaborators; mod commits; mod contributors; +mod dependabot; pub mod events; mod file; pub mod forks; @@ -34,6 +35,7 @@ pub use branches::ListBranchesBuilder; pub use collaborators::ListCollaboratorsBuilder; pub use commits::ListCommitsBuilder; pub use contributors::ListContributorsBuilder; +pub use dependabot::RepoDependabotAlertsHandler; pub use file::{DeleteFileBuilder, GetContentBuilder, UpdateFileBuilder}; pub use generate::GenerateRepositoryBuilder; pub use merges::MergeBranchBuilder; @@ -748,6 +750,11 @@ impl<'octo> RepoHandler<'octo> { RepoSecretsHandler::new(self) } + /// Handle dependabot alerts on the repository + pub fn dependabot(&self) -> RepoDependabotAlertsHandler<'_> { + RepoDependabotAlertsHandler::new(self) + } + /// Creates a new Git commit object. /// See https://docs.github.com/en/rest/git/commits?apiVersion=2022-11-28#create-a-commit /// ```no_run diff --git a/src/api/repos/dependabot.rs b/src/api/repos/dependabot.rs new file mode 100644 index 00000000..ec0b293c --- /dev/null +++ b/src/api/repos/dependabot.rs @@ -0,0 +1,177 @@ +use super::RepoHandler; + +/// A client to GitHub's repository dependabot API. +/// +/// Created with [`Octocrab::repos`]. +pub struct RepoDependabotAlertsHandler<'octo> { + handler: &'octo RepoHandler<'octo>, + params: Params, +} + +#[derive(serde::Serialize)] +struct Params { + #[serde(skip_serializing_if = "Option::is_none")] + per_page: Option, + #[serde(skip_serializing_if = "Option::is_none")] + page: Option, + #[serde(skip_serializing_if = "Option::is_none")] + state: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + severity: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + ecosystem: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + package: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + manifest: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + scope: Option, + #[serde(skip_serializing_if = "Option::is_none")] + sort: Option, + #[serde(skip_serializing_if = "Option::is_none")] + direction: Option, +} + +impl<'octo> RepoDependabotAlertsHandler<'octo> { + pub(crate) fn new(repo: &'octo RepoHandler<'octo>) -> Self { + Self { + handler: repo, + params: Params { + per_page: None, + page: None, + state: None, + severity: None, + ecosystem: None, + package: None, + manifest: None, + scope: None, + sort: None, + direction: None, + }, + } + } + + /// Lists all Dependabot Alerts available in a repository. + /// You must authenticate using an access token with the `repo` or `security_events` scope to use this endpoint. + /// ```no_run + /// # async fn run() -> octocrab::Result<()> { + /// # let octocrab = octocrab::Octocrab::default(); + /// let all_secrets = octocrab.repos("owner", "repo") + /// .dependabot() + /// .direction("asc") + /// .get_alerts() + /// .await?; + /// # Ok(()) + /// # } + pub async fn get_alerts( + &self, + ) -> crate::Result> { + let route = format!("/{}/dependabot/alerts", self.handler.repo); + self.handler.crab.get(route, Some(&self.params)).await + } + + /// Results per page (max 100). + pub fn per_page(mut self, per_page: impl Into) -> Self { + self.params.per_page = Some(per_page.into()); + self + } + + /// Page number of the results to fetch. + pub fn page(mut self, page: impl Into) -> Self { + self.params.page = Some(page.into()); + self + } + + /// Filter Dependabot Alerts by state. + pub fn state(mut self, state: impl Into>) -> Self { + self.params.state = Some(state.into()); + self + } + + /// Filter Dependabot Alerts by severity. + pub fn severity(mut self, severity: impl Into>) -> Self { + self.params.severity = Some(severity.into()); + self + } + + /// Filter Dependabot Alerts by ecosystem. + pub fn ecosystem(mut self, ecosystem: impl Into>) -> Self { + self.params.ecosystem = Some(ecosystem.into()); + self + } + + /// Filter Dependabot Alerts by package. + pub fn package(mut self, package: impl Into>) -> Self { + self.params.package = Some(package.into()); + self + } + + /// Filter Dependabot Alerts by manifest. + pub fn manifest(mut self, manifest: impl Into>) -> Self { + self.params.manifest = Some(manifest.into()); + self + } + + /// Filter Dependabot Alerts by scope. + pub fn scope(mut self, scope: impl Into) -> Self { + self.params.scope = Some(scope.into()); + self + } + + /// Sort Dependabot Alerts. + pub fn sort(mut self, sort: impl Into) -> Self { + self.params.sort = Some(sort.into()); + self + } + + /// Sort direction of Dependabot Alerts. + pub fn direction(mut self, direction: impl Into) -> Self { + self.params.direction = Some(direction.into()); + self + } + + /// Lists single Dependabot Alert for a repository. + /// You must authenticate using an access token with the `repo` or `security_events` scope to use this endpoint. + /// ```no_run + /// # async fn run() -> octocrab::Result<()> { + /// # let octocrab = octocrab::Octocrab::default(); + /// let all_secrets = octocrab.repos("owner", "repo") + /// .dependabot() + /// .get_alert(5) + /// .await?; + /// # Ok(()) + /// # } + pub async fn get_alert( + &self, + alert_number: u32, + ) -> crate::Result { + let route = format!("/{}/dependabot/alerts/{}", self.handler.repo, alert_number); + self.handler.crab.get(route, None::<&()>).await + } + + /// Updates a dependabot alert. + /// You must authenticate using an access token with the `security_events ` scope to use this endpoint. + /// ```no_run + /// # async fn run() -> octocrab::Result<()> { + /// # let octocrab = octocrab::Octocrab::default(); + /// use octocrab::models::repos::dependabot::UpdateDependabotAlert; + /// + /// let result = octocrab.repos("owner", "repo") + /// .dependabot() + /// .update_alert(5, &UpdateDependabotAlert{ + /// state: "dismissed", + /// dismissed_reason: Some("no_bandwidth"), + /// dismissed_comment: Some("I don't have time to fix this right now"), + /// }) + /// .await?; + /// # Ok(()) + /// # } + pub async fn update_alert( + &self, + alert_number: u32, + alert_update: Option<&crate::models::repos::dependabot::UpdateDependabotAlert<'_>>, + ) -> crate::Result { + let route = format!("/{}/dependabot/alerts/{}", self.handler.repo, alert_number); + self.handler.crab.patch(route, alert_update).await + } +} diff --git a/src/models/repos.rs b/src/models/repos.rs index 3d5eea6d..7f12a882 100644 --- a/src/models/repos.rs +++ b/src/models/repos.rs @@ -7,6 +7,7 @@ use hyper::Response; use snafu::ResultExt; use url::Url; +pub mod dependabot; pub mod secrets; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/src/models/repos/dependabot.rs b/src/models/repos/dependabot.rs new file mode 100644 index 00000000..7d2a650e --- /dev/null +++ b/src/models/repos/dependabot.rs @@ -0,0 +1,158 @@ +use super::super::*; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct DependabotAlert { + pub number: i64, + pub state: State, + pub dependency: Dependency, + pub security_advisory: SecurityAdvisory, + pub security_vulnerability: SecurityVulnerability, + pub url: Url, + pub html_url: Url, + pub created_at: DateTime, + pub updated_at: DateTime, + pub dismissed_at: Option>, + pub dismissed_by: Option, + pub dismissed_reason: Option, + pub dismissed_comment: Option, + pub fixed_at: Option>, + pub auto_dismissed_at: Option>, +} + +/*#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[non_exhaustive] +pub struct DependabotAlerts { + //pub total_count: i32, + pub alerts: Vec, +}*/ + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum DissmisedReason { + FixStarted, + Inaccurate, + NoBandwidth, + NotUsed, + TolerableRisk, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum State { + AutoDismissed, + Dismissed, + Fixed, + Open, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Dependency { + pub package: Package, + pub manifest_path: String, + pub scope: Option, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Scope { + Development, + Runtime, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Package { + pub ecosystem: String, + pub name: String, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SecurityAdvisory { + pub ghsa_id: String, + pub cve_id: Option, + pub summary: String, + pub description: String, + pub vulnerabilities: Vec, + pub severity: Severity, + pub cvss: Cvss, + pub cvss_severities: CvssSeverities, + pub cwes: Vec, + pub identifiers: Vec, + pub references: Vec, + pub published_at: DateTime, + pub updated_at: DateTime, + pub withdrawn_at: Option>, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Vulnerability { + pub package: Package, + pub severity: Severity, + pub vulnerable_version_range: String, + pub first_patched_version: Option, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Severity { + Low, + Medium, + High, + Critical, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct FirstPatchedVersion { + pub identifier: String, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Cvss { + pub vector_string: Option, + pub score: f64, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct CvssSeverities { + pub cvss_v3: Option, + pub cvss_v4: Option, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Cwe { + pub cwe_id: String, + pub name: String, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Identifier { + pub r#type: AdvisoryType, + pub value: String, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum AdvisoryType { + CVE, + GHSA, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Reference { + pub url: Url, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SecurityVulnerability { + pub package: Package, + pub severity: Severity, + pub vulnerable_version_range: String, + pub first_patched_version: Option, +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +pub struct UpdateDependabotAlert<'a> { + pub state: &'a str, + #[serde(skip_serializing_if = "Option::is_none")] + pub dismissed_reason: Option<&'a str>, + #[serde(skip_serializing_if = "Option::is_none")] + pub dismissed_comment: Option<&'a str>, +} diff --git a/tests/dependabot_alerts_test.rs b/tests/dependabot_alerts_test.rs new file mode 100644 index 00000000..23b6b143 --- /dev/null +++ b/tests/dependabot_alerts_test.rs @@ -0,0 +1,75 @@ +use wiremock::{ + matchers::{method, path}, + Mock, MockServer, ResponseTemplate, +}; + +use mock_error::setup_error_handler; +use octocrab::models::repos::dependabot::DependabotAlert; +use octocrab::Octocrab; + +mod mock_error; + +const OWNER: &str = "org"; +const REPO: &str = "some-repo"; + +async fn setup_dependabot_api(template: ResponseTemplate) -> MockServer { + let mock_server = MockServer::start().await; + + Mock::given(method("GET")) + .and(path(format!( + "/repos/{owner}/{repo}/dependabot/alerts", + owner = OWNER, + repo = REPO + ))) + .respond_with(template.clone()) + .mount(&mock_server) + .await; + setup_error_handler( + &mock_server, + &format!( + "GET on /repos/{owner}/{repo}/dependabot/alerts was not received", + owner = OWNER, + repo = REPO + ), + ) + .await; + + mock_server +} + +fn setup_octocrab(uri: &str) -> Octocrab { + Octocrab::builder().base_uri(uri).unwrap().build().unwrap() +} + +#[tokio::test] +async fn check_list_200() { + let s = include_str!("resources/check_dependabot_alerts.json"); + let alert: Vec = serde_json::from_str(s).unwrap(); + let template = ResponseTemplate::new(200).set_body_json(&alert); + let mock_server = setup_dependabot_api(template).await; + let client = setup_octocrab(&mock_server.uri()); + + let result = client + .repos(OWNER.to_owned(), REPO.to_owned()) + .dependabot() + .get_alerts() + .await; + + assert!( + result.is_ok(), + "expected successful result, got error: {:?}", + result + ); + + let response = result.unwrap(); + let items = response.items; + + assert_eq!(items.len(), 5); + + { + let item = &items[0]; + + assert_eq!(5, item.number); + assert_eq!(octocrab::models::repos::dependabot::State::Open, item.state); + } +} diff --git a/tests/resources/check_dependabot_alerts.json b/tests/resources/check_dependabot_alerts.json new file mode 100644 index 00000000..313d661f --- /dev/null +++ b/tests/resources/check_dependabot_alerts.json @@ -0,0 +1,802 @@ +[ + { + "number": 5, + "state": "open", + "dependency": { + "package": { + "ecosystem": "npm", + "name": "lodash" + }, + "manifest_path": "javascript/yarn.lock", + "scope": "runtime" + }, + "security_advisory": { + "ghsa_id": "GHSA-35jh-r3h4-6jhm", + "cve_id": "CVE-2021-23337", + "summary": "Command Injection in lodash", + "description": "`lodash` versions prior to 4.17.21 are vulnerable to Command Injection via the template function.", + "severity": "high", + "identifiers": [ + { + "value": "GHSA-35jh-r3h4-6jhm", + "type": "GHSA" + }, + { + "value": "CVE-2021-23337", + "type": "CVE" + } + ], + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-23337" + }, + { + "url": "https://github.com/lodash/lodash/commit/3469357cff396a26c363f8c1b5a91dde28ba4b1c" + }, + { + "url": "https://snyk.io/vuln/SNYK-JS-LODASH-1040724" + }, + { + "url": "https://github.com/lodash/lodash/blob/ddfd9b11a0126db2302cb70ec9973b66baec0975/lodash.js#L14851" + }, + { + "url": "https://github.com/lodash/lodash/blob/ddfd9b11a0126db2302cb70ec9973b66baec0975/lodash.js%23L14851" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGFUJIONWEBJARS-1074932" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARS-1074930" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARSBOWER-1074928" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARSBOWERGITHUBLODASH-1074931" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARSNPM-1074929" + }, + { + "url": "https://www.oracle.com//security-alerts/cpujul2021.html" + }, + { + "url": "https://www.oracle.com/security-alerts/cpuoct2021.html" + }, + { + "url": "https://www.oracle.com/security-alerts/cpujan2022.html" + }, + { + "url": "https://www.oracle.com/security-alerts/cpujul2022.html" + }, + { + "url": "https://cert-portal.siemens.com/productcert/pdf/ssa-637483.pdf" + }, + { + "url": "https://security.netapp.com/advisory/ntap-20210312-0006" + }, + { + "url": "https://github.com/advisories/GHSA-35jh-r3h4-6jhm" + } + ], + "published_at": "2021-05-06T16:05:51Z", + "updated_at": "2024-04-17T18:39:19Z", + "withdrawn_at": null, + "vulnerabilities": [ + { + "package": { + "ecosystem": "npm", + "name": "lodash" + }, + "severity": "high", + "vulnerable_version_range": "< 4.17.21", + "first_patched_version": { + "identifier": "4.17.21" + } + }, + { + "package": { + "ecosystem": "npm", + "name": "lodash-es" + }, + "severity": "high", + "vulnerable_version_range": "< 4.17.21", + "first_patched_version": { + "identifier": "4.17.21" + } + }, + { + "package": { + "ecosystem": "npm", + "name": "lodash.template" + }, + "severity": "high", + "vulnerable_version_range": "<= 4.5.0", + "first_patched_version": null + }, + { + "package": { + "ecosystem": "npm", + "name": "lodash-template" + }, + "severity": "high", + "vulnerable_version_range": "<= 1.0.0", + "first_patched_version": null + } + ], + "cvss": { + "vector_string": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H", + "score": 7.2 + }, + "cvss_severities": { + "cvss_v3": { + "vector_string": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H", + "score": 7.2 + }, + "cvss_v4": { + "vector_string": null, + "score": 0.0 + } + }, + "cwes": [ + { + "cwe_id": "CWE-77", + "name": "Improper Neutralization of Special Elements used in a Command ('Command Injection')" + }, + { + "cwe_id": "CWE-94", + "name": "Improper Control of Generation of Code ('Code Injection')" + } + ] + }, + "security_vulnerability": { + "package": { + "ecosystem": "npm", + "name": "lodash" + }, + "severity": "high", + "vulnerable_version_range": "< 4.17.21", + "first_patched_version": { + "identifier": "4.17.21" + } + }, + "url": "https://api.github.com/repos/octocat/hello-world/dependabot/alerts/5", + "html_url": "https://github.com/octocat/hello-world/security/dependabot/5", + "created_at": "2024-10-07T11:59:33Z", + "updated_at": "2024-10-07T11:59:33Z", + "dismissed_at": null, + "dismissed_by": null, + "dismissed_reason": null, + "dismissed_comment": null, + "fixed_at": null, + "auto_dismissed_at": null + }, + { + "number": 4, + "state": "open", + "dependency": { + "package": { + "ecosystem": "npm", + "name": "lodash" + }, + "manifest_path": "javascript/yarn.lock", + "scope": "runtime" + }, + "security_advisory": { + "ghsa_id": "GHSA-29mw-wpgm-hmr9", + "cve_id": "CVE-2020-28500", + "summary": "Regular Expression Denial of Service (ReDoS) in lodash", + "description": "All versions of package lodash prior to 4.17.21 are vulnerable to Regular Expression Denial of Service (ReDoS) via the `toNumber`, `trim` and `trimEnd` functions. \n\nSteps to reproduce (provided by reporter Liyuan Chen):\n```js\nvar lo = require('lodash');\n\nfunction build_blank(n) {\n var ret = \"1\"\n for (var i = 0; i < n; i++) {\n ret += \" \"\n }\n return ret + \"1\";\n}\nvar s = build_blank(50000) var time0 = Date.now();\nlo.trim(s) \nvar time_cost0 = Date.now() - time0;\nconsole.log(\"time_cost0: \" + time_cost0);\nvar time1 = Date.now();\nlo.toNumber(s) var time_cost1 = Date.now() - time1;\nconsole.log(\"time_cost1: \" + time_cost1);\nvar time2 = Date.now();\nlo.trimEnd(s);\nvar time_cost2 = Date.now() - time2;\nconsole.log(\"time_cost2: \" + time_cost2);\n```", + "severity": "medium", + "identifiers": [ + { + "value": "GHSA-29mw-wpgm-hmr9", + "type": "GHSA" + }, + { + "value": "CVE-2020-28500", + "type": "CVE" + } + ], + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-28500" + }, + { + "url": "https://github.com/lodash/lodash/pull/5065" + }, + { + "url": "https://github.com/lodash/lodash/pull/5065/commits/02906b8191d3c100c193fe6f7b27d1c40f200bb7" + }, + { + "url": "https://github.com/lodash/lodash/blob/npm/trimEnd.js%23L8" + }, + { + "url": "https://security.netapp.com/advisory/ntap-20210312-0006/" + }, + { + "url": "https://snyk.io/vuln/SNYK-JS-LODASH-1018905" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGFUJIONWEBJARS-1074896" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARS-1074894" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARSBOWER-1074892" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARSBOWERGITHUBLODASH-1074895" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARSNPM-1074893" + }, + { + "url": "https://www.oracle.com//security-alerts/cpujul2021.html" + }, + { + "url": "https://www.oracle.com/security-alerts/cpuoct2021.html" + }, + { + "url": "https://www.oracle.com/security-alerts/cpujan2022.html" + }, + { + "url": "https://www.oracle.com/security-alerts/cpujul2022.html" + }, + { + "url": "https://cert-portal.siemens.com/productcert/pdf/ssa-637483.pdf" + }, + { + "url": "https://github.com/lodash/lodash/commit/c4847ebe7d14540bb28a8b932a9ce1b9ecbfee1a" + }, + { + "url": "https://github.com/advisories/GHSA-29mw-wpgm-hmr9" + } + ], + "published_at": "2022-01-06T20:30:46Z", + "updated_at": "2023-11-01T23:21:12Z", + "withdrawn_at": null, + "vulnerabilities": [ + { + "package": { + "ecosystem": "npm", + "name": "lodash" + }, + "severity": "medium", + "vulnerable_version_range": "< 4.17.21", + "first_patched_version": { + "identifier": "4.17.21" + } + }, + { + "package": { + "ecosystem": "npm", + "name": "lodash-es" + }, + "severity": "medium", + "vulnerable_version_range": "< 4.17.21", + "first_patched_version": { + "identifier": "4.17.21" + } + }, + { + "package": { + "ecosystem": "npm", + "name": "lodash.trimend" + }, + "severity": "medium", + "vulnerable_version_range": "<= 4.5.1", + "first_patched_version": null + }, + { + "package": { + "ecosystem": "npm", + "name": "lodash.trim" + }, + "severity": "medium", + "vulnerable_version_range": "<= 4.5.1", + "first_patched_version": null + } + ], + "cvss": { + "vector_string": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L", + "score": 5.3 + }, + "cvss_severities": { + "cvss_v3": { + "vector_string": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L", + "score": 5.3 + }, + "cvss_v4": { + "vector_string": null, + "score": 0.0 + } + }, + "cwes": [ + { + "cwe_id": "CWE-400", + "name": "Uncontrolled Resource Consumption" + }, + { + "cwe_id": "CWE-1333", + "name": "Inefficient Regular Expression Complexity" + } + ] + }, + "security_vulnerability": { + "package": { + "ecosystem": "npm", + "name": "lodash" + }, + "severity": "medium", + "vulnerable_version_range": "< 4.17.21", + "first_patched_version": { + "identifier": "4.17.21" + } + }, + "url": "https://api.github.com/repos/octocat/hello-world/dependabot/alerts/4", + "html_url": "https://github.com/octocat/hello-world/security/dependabot/4", + "created_at": "2024-10-07T11:59:33Z", + "updated_at": "2024-10-07T11:59:33Z", + "dismissed_at": null, + "dismissed_by": null, + "dismissed_reason": null, + "dismissed_comment": null, + "fixed_at": null, + "auto_dismissed_at": null + }, + { + "number": 3, + "state": "open", + "dependency": { + "package": { + "ecosystem": "npm", + "name": "lodash" + }, + "manifest_path": "javascript/package-lock.json", + "scope": "runtime" + }, + "security_advisory": { + "ghsa_id": "GHSA-35jh-r3h4-6jhm", + "cve_id": "CVE-2021-23337", + "summary": "Command Injection in lodash", + "description": "`lodash` versions prior to 4.17.21 are vulnerable to Command Injection via the template function.", + "severity": "high", + "identifiers": [ + { + "value": "GHSA-35jh-r3h4-6jhm", + "type": "GHSA" + }, + { + "value": "CVE-2021-23337", + "type": "CVE" + } + ], + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-23337" + }, + { + "url": "https://github.com/lodash/lodash/commit/3469357cff396a26c363f8c1b5a91dde28ba4b1c" + }, + { + "url": "https://snyk.io/vuln/SNYK-JS-LODASH-1040724" + }, + { + "url": "https://github.com/lodash/lodash/blob/ddfd9b11a0126db2302cb70ec9973b66baec0975/lodash.js#L14851" + }, + { + "url": "https://github.com/lodash/lodash/blob/ddfd9b11a0126db2302cb70ec9973b66baec0975/lodash.js%23L14851" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGFUJIONWEBJARS-1074932" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARS-1074930" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARSBOWER-1074928" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARSBOWERGITHUBLODASH-1074931" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARSNPM-1074929" + }, + { + "url": "https://www.oracle.com//security-alerts/cpujul2021.html" + }, + { + "url": "https://www.oracle.com/security-alerts/cpuoct2021.html" + }, + { + "url": "https://www.oracle.com/security-alerts/cpujan2022.html" + }, + { + "url": "https://www.oracle.com/security-alerts/cpujul2022.html" + }, + { + "url": "https://cert-portal.siemens.com/productcert/pdf/ssa-637483.pdf" + }, + { + "url": "https://security.netapp.com/advisory/ntap-20210312-0006" + }, + { + "url": "https://github.com/advisories/GHSA-35jh-r3h4-6jhm" + } + ], + "published_at": "2021-05-06T16:05:51Z", + "updated_at": "2024-04-17T18:39:19Z", + "withdrawn_at": null, + "vulnerabilities": [ + { + "package": { + "ecosystem": "npm", + "name": "lodash" + }, + "severity": "high", + "vulnerable_version_range": "< 4.17.21", + "first_patched_version": { + "identifier": "4.17.21" + } + }, + { + "package": { + "ecosystem": "npm", + "name": "lodash-es" + }, + "severity": "high", + "vulnerable_version_range": "< 4.17.21", + "first_patched_version": { + "identifier": "4.17.21" + } + }, + { + "package": { + "ecosystem": "npm", + "name": "lodash.template" + }, + "severity": "high", + "vulnerable_version_range": "<= 4.5.0", + "first_patched_version": null + }, + { + "package": { + "ecosystem": "npm", + "name": "lodash-template" + }, + "severity": "high", + "vulnerable_version_range": "<= 1.0.0", + "first_patched_version": null + } + ], + "cvss": { + "vector_string": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H", + "score": 7.2 + }, + "cvss_severities": { + "cvss_v3": { + "vector_string": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H", + "score": 7.2 + }, + "cvss_v4": { + "vector_string": null, + "score": 0.0 + } + }, + "cwes": [ + { + "cwe_id": "CWE-77", + "name": "Improper Neutralization of Special Elements used in a Command ('Command Injection')" + }, + { + "cwe_id": "CWE-94", + "name": "Improper Control of Generation of Code ('Code Injection')" + } + ] + }, + "security_vulnerability": { + "package": { + "ecosystem": "npm", + "name": "lodash" + }, + "severity": "high", + "vulnerable_version_range": "< 4.17.21", + "first_patched_version": { + "identifier": "4.17.21" + } + }, + "url": "https://api.github.com/repos/octocat/hello-world/dependabot/alerts/3", + "html_url": "https://github.com/octocat/hello-world/security/dependabot/3", + "created_at": "2024-10-07T11:59:33Z", + "updated_at": "2024-10-07T11:59:33Z", + "dismissed_at": null, + "dismissed_by": null, + "dismissed_reason": null, + "dismissed_comment": null, + "fixed_at": null, + "auto_dismissed_at": null + }, + { + "number": 2, + "state": "open", + "dependency": { + "package": { + "ecosystem": "npm", + "name": "lodash" + }, + "manifest_path": "javascript/package-lock.json", + "scope": "runtime" + }, + "security_advisory": { + "ghsa_id": "GHSA-29mw-wpgm-hmr9", + "cve_id": "CVE-2020-28500", + "summary": "Regular Expression Denial of Service (ReDoS) in lodash", + "description": "All versions of package lodash prior to 4.17.21 are vulnerable to Regular Expression Denial of Service (ReDoS) via the `toNumber`, `trim` and `trimEnd` functions. \n\nSteps to reproduce (provided by reporter Liyuan Chen):\n```js\nvar lo = require('lodash');\n\nfunction build_blank(n) {\n var ret = \"1\"\n for (var i = 0; i < n; i++) {\n ret += \" \"\n }\n return ret + \"1\";\n}\nvar s = build_blank(50000) var time0 = Date.now();\nlo.trim(s) \nvar time_cost0 = Date.now() - time0;\nconsole.log(\"time_cost0: \" + time_cost0);\nvar time1 = Date.now();\nlo.toNumber(s) var time_cost1 = Date.now() - time1;\nconsole.log(\"time_cost1: \" + time_cost1);\nvar time2 = Date.now();\nlo.trimEnd(s);\nvar time_cost2 = Date.now() - time2;\nconsole.log(\"time_cost2: \" + time_cost2);\n```", + "severity": "medium", + "identifiers": [ + { + "value": "GHSA-29mw-wpgm-hmr9", + "type": "GHSA" + }, + { + "value": "CVE-2020-28500", + "type": "CVE" + } + ], + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-28500" + }, + { + "url": "https://github.com/lodash/lodash/pull/5065" + }, + { + "url": "https://github.com/lodash/lodash/pull/5065/commits/02906b8191d3c100c193fe6f7b27d1c40f200bb7" + }, + { + "url": "https://github.com/lodash/lodash/blob/npm/trimEnd.js%23L8" + }, + { + "url": "https://security.netapp.com/advisory/ntap-20210312-0006/" + }, + { + "url": "https://snyk.io/vuln/SNYK-JS-LODASH-1018905" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGFUJIONWEBJARS-1074896" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARS-1074894" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARSBOWER-1074892" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARSBOWERGITHUBLODASH-1074895" + }, + { + "url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARSNPM-1074893" + }, + { + "url": "https://www.oracle.com//security-alerts/cpujul2021.html" + }, + { + "url": "https://www.oracle.com/security-alerts/cpuoct2021.html" + }, + { + "url": "https://www.oracle.com/security-alerts/cpujan2022.html" + }, + { + "url": "https://www.oracle.com/security-alerts/cpujul2022.html" + }, + { + "url": "https://cert-portal.siemens.com/productcert/pdf/ssa-637483.pdf" + }, + { + "url": "https://github.com/lodash/lodash/commit/c4847ebe7d14540bb28a8b932a9ce1b9ecbfee1a" + }, + { + "url": "https://github.com/advisories/GHSA-29mw-wpgm-hmr9" + } + ], + "published_at": "2022-01-06T20:30:46Z", + "updated_at": "2023-11-01T23:21:12Z", + "withdrawn_at": null, + "vulnerabilities": [ + { + "package": { + "ecosystem": "npm", + "name": "lodash" + }, + "severity": "medium", + "vulnerable_version_range": "< 4.17.21", + "first_patched_version": { + "identifier": "4.17.21" + } + }, + { + "package": { + "ecosystem": "npm", + "name": "lodash-es" + }, + "severity": "medium", + "vulnerable_version_range": "< 4.17.21", + "first_patched_version": { + "identifier": "4.17.21" + } + }, + { + "package": { + "ecosystem": "npm", + "name": "lodash.trimend" + }, + "severity": "medium", + "vulnerable_version_range": "<= 4.5.1", + "first_patched_version": null + }, + { + "package": { + "ecosystem": "npm", + "name": "lodash.trim" + }, + "severity": "medium", + "vulnerable_version_range": "<= 4.5.1", + "first_patched_version": null + } + ], + "cvss": { + "vector_string": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L", + "score": 5.3 + }, + "cvss_severities": { + "cvss_v3": { + "vector_string": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L", + "score": 5.3 + }, + "cvss_v4": { + "vector_string": null, + "score": 0.0 + } + }, + "cwes": [ + { + "cwe_id": "CWE-400", + "name": "Uncontrolled Resource Consumption" + }, + { + "cwe_id": "CWE-1333", + "name": "Inefficient Regular Expression Complexity" + } + ] + }, + "security_vulnerability": { + "package": { + "ecosystem": "npm", + "name": "lodash" + }, + "severity": "medium", + "vulnerable_version_range": "< 4.17.21", + "first_patched_version": { + "identifier": "4.17.21" + } + }, + "url": "https://api.github.com/repos/octocat/hello-world/dependabot/alerts/2", + "html_url": "https://github.com/octocat/hello-world/security/dependabot/2", + "created_at": "2024-10-07T11:59:32Z", + "updated_at": "2024-10-07T11:59:32Z", + "dismissed_at": null, + "dismissed_by": null, + "dismissed_reason": null, + "dismissed_comment": null, + "fixed_at": null, + "auto_dismissed_at": null + }, + { + "number": 1, + "state": "open", + "dependency": { + "package": { + "ecosystem": "npm", + "name": "hot-formula-parser" + }, + "manifest_path": "javascript/package-lock.json", + "scope": "runtime" + }, + "security_advisory": { + "ghsa_id": "GHSA-rc77-xxq6-4mff", + "cve_id": "CVE-2020-6836", + "summary": "Command Injection in hot-formula-parser", + "description": "Versions of `hot-formula-parser` prior to 3.0.1 are vulnerable to Command Injection. The package fails to sanitize values passed to the `parse` function and concatenates it in an `eval` call. If a value of the formula is supplied by user-controlled input it may allow attackers to run arbitrary commands in the server. \nParsing the following formula creates a `test` file in the present directory: \n`\"SUM([(function(){require('child_process').execSync('touch test')})(),2])\"`\n\n\n## Recommendation\n\nUpgrade to version 3.0.1 or later.", + "severity": "critical", + "identifiers": [ + { + "value": "GHSA-rc77-xxq6-4mff", + "type": "GHSA" + }, + { + "value": "CVE-2020-6836", + "type": "CVE" + } + ], + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-6836" + }, + { + "url": "https://github.com/handsontable/formula-parser/pull/58" + }, + { + "url": "https://github.com/handsontable/formula-parser/commit/396b089738d4bf30eb570a4fe6a188affa95cd5e" + }, + { + "url": "https://blog.truesec.com/2020/01/17/reverse-shell-through-a-node-js-math-parser/" + }, + { + "url": "https://www.npmjs.com/advisories/1439" + }, + { + "url": "https://github.com/advisories/GHSA-rc77-xxq6-4mff" + } + ], + "published_at": "2020-05-06T19:32:33Z", + "updated_at": "2023-01-09T05:02:15Z", + "withdrawn_at": null, + "vulnerabilities": [ + { + "package": { + "ecosystem": "npm", + "name": "hot-formula-parser" + }, + "severity": "critical", + "vulnerable_version_range": "< 3.0.1", + "first_patched_version": { + "identifier": "3.0.1" + } + } + ], + "cvss": { + "vector_string": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", + "score": 9.8 + }, + "cvss_severities": { + "cvss_v3": { + "vector_string": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", + "score": 9.8 + }, + "cvss_v4": { + "vector_string": null, + "score": 0.0 + } + }, + "cwes": [ + { + "cwe_id": "CWE-94", + "name": "Improper Control of Generation of Code ('Code Injection')" + } + ] + }, + "security_vulnerability": { + "package": { + "ecosystem": "npm", + "name": "hot-formula-parser" + }, + "severity": "critical", + "vulnerable_version_range": "< 3.0.1", + "first_patched_version": { + "identifier": "3.0.1" + } + }, + "url": "https://api.github.com/repos/octocat/hello-world/dependabot/alerts/1", + "html_url": "https://github.com/octocat/hello-world/security/dependabot/1", + "created_at": "2024-10-07T11:59:32Z", + "updated_at": "2024-10-07T11:59:32Z", + "dismissed_at": null, + "dismissed_by": null, + "dismissed_reason": null, + "dismissed_comment": null, + "fixed_at": null, + "auto_dismissed_at": null + } +]