How to prioritize and remediate vulnerabilities using Tidelift APIs

Today we're going to show how you can use Tidelift's to research a piece of software you're using, analyze a vulnerability that affects it, and determine how you want to prioritize it based on first-party information direct from the maintainer.

What we'll need

For this exercise, we'll use:

  • A Tidelift API key, stored in the environment variable TIDELIFT_API_KEY.
  • The commandline utilitiy curl. You likely already have this installed on your system.

(If you'd like to know more about integrating these API calls with your own code and systems using your programming language of choice, see How to access Tidelift APIs.)

Step 1: Investigate our package

We're using the NPM package qs, version 6.9.0, in our application.

First, we want to know about the package itself. To query information on the qs package, we're going to use the Tidelift package API, with the following command:

curl -H "Accept: application/json" -H "Authorization: bearer $TIDELIFT_API_KEY" \
https://api.tidelift.com/external-api/v1/packages/npm/qs

Here's what we get back (note: the release list has been trimmed for space).

Full package API output (click to expand)

{
  "name": "qs",
  "platform": "npm",
  "purl": "pkg:npm/qs",
  "description": "A querystring parser that supports nesting and arrays, with a depth limit",
  "tidelift_recommendation": "recommended",
  "versioning_scheme": "semver",
  "security_policy_url": "https://tidelift.com/docs/security",
  "contributors_count": 62,
  "package_manager_url": "https://www.npmjs.com/package/qs",
  "sdlc_policy": "https://support.tidelift.com/hc/en-us/articles/4406288074260-Lifter-tasks-overview",
  "sdlc_evidence": "https://support.tidelift.com/hc/en-us/articles/4406288074260-Lifter-tasks-overview",
  "repository": {
    "url": "https://github.com/ljharb/qs",
    "source": "package_manager"
  },
  "license": {
    "expression": "BSD-3-Clause",
    "source": "human_verified"
  },
  "latest_release": {
    "version": "6.11.2",
    "published_at": "2023-05-15T16:08:31.665Z"
  },
  "latest_stable_release": {
    "version": "6.11.2",
    "published_at": "2023-05-15T16:08:31.665Z"
  },
  "latest_recommended_release": {
    "version": "6.11.2",
    "published_at": "2023-05-15T16:08:31.665Z"
  },
  "releases": [
    {
      "version": "6.11.2",
      "published_at": "2023-05-15T16:08:31.665Z",
      "tidelift_recommendation": "recommended"
    },
    {
      "version": "6.11.1",
      "published_at": "2023-03-06T22:25:03.220Z",
      "tidelift_recommendation": "recommended"
    },
    {
      "version": "6.11.0",
      "published_at": "2022-06-27T04:49:53.976Z",
      "tidelift_recommendation": "recommended"
    },
    {
      "version": "6.10.5",
      "published_at": "2022-06-06T22:52:18.062Z",
      "tidelift_recommendation": "recommended"
    },
    {
      "version": "6.10.4",
      "published_at": "2022-06-06T19:07:09.851Z",
      "tidelift_recommendation": "recommended"
    },
...
    {
      "version": "6.9.1",
      "published_at": "2019-11-08T06:46:35.355Z",
      "tidelift_recommendation": "not_recommended"
    },
    {
      "version": "6.9.0",
      "published_at": "2019-09-21T22:26:19.096Z",
      "tidelift_recommendation": "not_recommended"
    },
    {
      "version": "6.8.3",
      "published_at": "2022-01-11T05:59:46.325Z",
      "tidelift_recommendation": "recommended"
    },
    {
      "version": "6.8.2",
      "published_at": "2020-03-25T19:47:25.334Z",
      "tidelift_recommendation": "not_recommended"
    },
...
  ],
  "repository_statistics": {
    "last_52_weeks_contributors": 4,
    "last_commit_at": "2024-01-27",
    "one_year_closed_pull_requests": 3,
    "one_year_total_pull_requests": 5,
    "one_year_closed_issues": 13,
    "one_year_total_issues": 26,
    "status": "Deprecated"
  },
  "quality_checks": {
    "no_executable_artifacts_in_the_source_repository": {
      "status": "passed"
    },
    "no_known_vulnerabilities_on_latest_release": {
      "status": "passed"
    },
    "no_known_issues_in_dependencies_for_latest_release": {
      "status": "passed"
    },
    "discoverable_security_policy": {
      "status": "passed"
    },
    "two_factor_authentication_at_source_repository": {
      "status": "passed"
    },
    "two_factor_authentication_for_package_manager": {
      "status": "passed"
    },
    "release_managers_are_reviewed": {
      "status": "passed"
    },
    "package_has_a_defined_machine_readable_license": {
      "status": "passed"
    },
    "package_has_a_clean_release_available": {
      "status": "passed"
    },
    "package_is_not_deprecated": {
      "status": "passed"
    },
    "package_appears_maintained": {
      "status": "passed"
    },
    "package_has_a_stable_release_greater_than_two_years_old": {
      "status": "passed"
    },
    "releases_are_discoverable_upstream": {
      "status": "passed"
    },
    "responsive_to_security_issues": {
      "status": "passed"
    },
    "security_vulnerabilities_have_recommendations": {
      "status": "passed"
    },
    "uses_code_review_practices": {
      "status": "not_passed"
    },
    "uses_fuzzing_tools": {
      "status": "not_passed"
    }
  }
}

Looking at this API output, here are key conclusions we can draw:

  "tidelift_recommendation": "recommended",

This package is recommended; it passes Tidelift's package standards on whether it's a good package to use.

    {
      "version": "6.9.0",
      "published_at": "2019-09-21T22:26:19.096Z",
      "tidelift_recommendation": "not_recommended"
    },

The version we're looking for, however, is not recommended by Tidelift's release standards. It has some issues.

Step 2: Investigate our release, and why it's not recommended

Let's look into release 6.9.0 with the Tidelift release API, with the following command:

curl -H "Accept: application/json" -H "Authorization: bearer $TIDELIFT_API_KEY" \
https://api.tidelift.com/external-api/v1/packages/npm/qs/releases/6.9.0

Here's what we get back:

Full release API output (click to expand)
{
  "name": "qs",
  "platform": "npm",
  "version": "6.9.0",
  "published_at": "2019-09-21T22:26:19.096Z",
  "purl": "pkg:npm/qs@6.9.0",
  "repository": {
    "url": "https://github.com/ljharb/qs",
    "source": "package_manager"
  },
  "tidelift_recommendation": "not_recommended",
  "violations": [
    {
      "catalog_standard": "vulnerabilities",
      "violation_id": "27ede3f1-8cab-4b02-afc4-55aac784dc6c",
      "title": "vulnerability CVE-2022-24999 with severity 7.5 is present",
      "package_name": "qs",
      "package_platform": "npm",
      "vulnerability": {
        "id": "CVE-2022-24999",
        "severity": "7.5",
        "description": "qs before 6.10.3, as used in Express before 4.17.3 and other products, allows attackers to cause a Node process hang for an Express application because an __ proto__ key can be used. In many typical Express use cases, an unauthenticated remote attacker can place the attack payload in the query string of the URL that is used to visit the application, such as a[__proto__]=b&a[__proto__]&a[length]=100000000. The fix was backported to qs 6.9.7, 6.8.3, 6.7.3, 6.6.1, 6.5.3, 6.4.1, 6.3.3, and 6.2.4 (and therefore Express 4.17.3, which has \"deps: qs@6.9.7\" in its release description, is not vulnerable).",
        "date": "2022-11-26",
        "url": "https://github.com/expressjs/express/releases/tag/4.17.3",
        "severity_rating": "High"
      }
    }
  ],
  "license": {
    "expression": "BSD-3-Clause",
    "source": "verified_by_maintainer"
  },
  "nearest_recommended_release": {
    "version": "6.9.7",
    "published_at": "2022-01-11T05:59:51.851Z"
  }
}

Digging into this data, we learn:

  "violations": [
    {
      "catalog_standard": "vulnerabilities",
      "violation_id": "27ede3f1-8cab-4b02-afc4-55aac784dc6c",
      "title": "vulnerability CVE-2022-24999 with severity 7.5 is present",
      "package_name": "qs",
      "package_platform": "npm",
      "vulnerability": {
        "id": "CVE-2022-24999",
        "severity": "7.5",
        "description": "qs before 6.10.3, as used in Express before 4.17.3 and other products, allows attackers to cause a Node process hang for an Express application because an __ proto__ key can be used. In many typical Express use cases, an unauthenticated remote attacker can place the attack payload in the query string of the URL that is used to visit the application, such as a[__proto__]=b&a[__proto__]&a[length]=100000000. The fix was backported to qs 6.9.7, 6.8.3, 6.7.3, 6.6.1, 6.5.3, 6.4.1, 6.3.3, and 6.2.4 (and therefore Express 4.17.3, which has \"deps: qs@6.9.7\" in its release description, is not vulnerable).",
        "date": "2022-11-26",
        "url": "https://github.com/expressjs/express/releases/tag/4.17.3",
        "severity_rating": "High"
      }
    }

It's not recommended because it has a vulnerability: CVE-2022-24999. It's rated high severity, 7.5.

  "nearest_recommended_release": {
    "version": "6.9.7",
    "published_at": "2022-01-11T05:59:51.851Z"
  }

The nearest recommended release that we should upgrade to, is version 6.9.7. That's good information for my engineers.

Step 3: Investigate the vulnerability, and get insights directly from the maintainer

But we've got a lot of vulnerability backlog to work through, so we'd like more information to know how important it is to prioritize this particular fix. It would be great to get a recommendation straight from the upstream maintainer on what to do.

Luckily for us, Tidelift provides that. Let's look up CVE-2022-24999 in Tidelift's vulnerability API, with the following command:

curl -H "Accept: application/json" -H "Authorization: bearer $TIDELIFT_API_KEY" \
https://api.tidelift.com/external-api/v1/vulnerabilities/CVE-2022-20499

Here's what we get back:

Full vulnerability API output (click to expand)

{
  "vuln_id": "CVE-2022-24999",
  "url": "https://github.com/expressjs/express/releases/tag/4.17.3",
  "nist_url": "https://nvd.nist.gov/vuln/detail/CVE-2022-24999",
  "description": "qs before 6.10.3, as used in Express before 4.17.3 and other products, allows attackers to cause a Node process hang for an Express application because an __ proto__ key can be used. In many typical Express use cases, an unauthenticated remote attacker can place the attack payload in the query string of the URL that is used to visit the application, such as a[__proto__]=b&a[__proto__]&a[length]=100000000. The fix was backported to qs 6.9.7, 6.8.3, 6.7.3, 6.6.1, 6.5.3, 6.4.1, 6.3.3, and 6.2.4 (and therefore Express 4.17.3, which has \"deps: qs@6.9.7\" in its release description, is not vulnerable).",
  "severity": "7.5",
  "affected_packages": [
    {
      "package": "npm/express",
      "recommendation": "upgrade",
      "recommendation_details": null,
      "unaffected_versions": "= 4.17.3"
    },
    {
      "package": "npm/qs",
      "recommendation": "upgrade_or_workaround",
      "recommendation_details": {
        "id": "e11d7f9f-bf79-4149-bcf7-7b4a276af45a",
        "impact_score": 10,
        "impact_description": "See above.",
        "real_issue": true,
        "false_positive_reason": "",
        "includes_dev": false,
        "specific_methods_affected": true,
        "specific_methods_description": "`qs.parse`",
        "other_conditions": true,
        "other_conditions_description": "If you feed untrusted input into `qs.parse`",
        "workaround_available": true,
        "workaround_description": "Reject any query string that contains `__proto__` prior to sending it into `qs.parse`.",
        "visibility": "external",
        "created_at": "2022-11-28T23:45:36.623Z",
        "updated_at": "2023-04-06T12:12:36.258Z"
      },
      "unaffected_versions": "= 6.2.4, = 6.3.3, = 6.4.1, = 6.5.3, = 6.6.1, = 6.7.3, = 6.8.3, = 6.9.7, = 6.10.3"
    }
  ]
}

This recommendation comes straight from the upstream maintainer, Jordan Harband.

Looking at this recommendations, we note:

        "impact_score": 10,

This is a real issue ; we should prioritize handling it. However...

        "workaround_available": true,
        "workaround_description": "Reject any query string that contains `__proto__` prior to sending it into `qs.parse`.",

We have a short-term workaround we can apply in our code if we cannot upgrade to the nearest recommended release (6.9.7) right away. Again, more information I can hand directly to our developers.

Recap

We were using the qs-6.9.0 release in our software. With just an API key, curl, and a few short API calls:

  • We learned that while this is a good package to build on, we shouldn't be using this release
  • We learned why (it has a vulnerability)
  • We got an assessment of the vulnerability and a workaround, direct from the maintainer
  • We know what the smallest upgrade we have to apply to be using a clean, recommended release

This information can now go straight to my development team for prioritization, and I've made our software stack cleaner and reduced our risk.

Was this article helpful?
0 out of 0 found this helpful

Comments

0 comments

Article is closed for comments.

Articles in this section