Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(parser): add conan parser #4569

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cve_bin_tool/parsers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"perl",
"dart",
"env",
"ccpp",
]


Expand Down
65 changes: 65 additions & 0 deletions cve_bin_tool/parsers/ccpp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright (C) 2024 Intel Corporation
# SPDX-License-Identifier: GPL-3.0-or-later
"""Python script containing all functionalities related to parsing of C/C++ conan.lock files."""

import json
import re

from cve_bin_tool.parsers import Parser


class CCppParser(Parser):
"""
Parser for C/C++ conan.lock files based on
https://docs.conan.io/2/tutorial/versioning/lockfiles.html
"""

PARSER_MATCH_FILENAMES = [
"conan.lock",
]

def __init__(self, cve_db, logger):
super().__init__(cve_db, logger)
self.purl_pkg_type = "conan"

def generate_purl(self, product, vendor="", version="", qualifier={}, subpath=None):
"""Generates PURL after normalizing all components."""
product = re.sub(r"[^a-zA-Z0-9._-]", "", product).lower()

if not product:
return None

purl = super().generate_purl(
product,
vendor,
version,
qualifier,
subpath,
)

return purl

def run_checker(self, filename):
"""Parse the file and yield valid PURLs."""
self.filename = filename
with open(self.filename) as fh:
data = json.load(fh)
requires = data["requires"]
build_requires = data["build_requires"]
if requires:
for require in requires:
product = require.split("#")[0].split("/")[0]
version = require.split("#")[0].split("/")[1]
purl = self.generate_purl(product)
vendor = self.get_vendor(purl, product, version)
if vendor is not None:
yield from vendor
if build_requires:
AryanBakliwal marked this conversation as resolved.
Show resolved Hide resolved
for build_require in build_requires:
product = build_require.split("#")[0].split("/")[0]
version = build_require.split("#")[0].split("/")[1]
purl = self.generate_purl(product)
vendor = self.get_vendor(purl, product, version)
if vendor is not None:
yield from vendor
self.logger.debug(f"Done scanning file: {self.filename}")
9 changes: 8 additions & 1 deletion doc/MANUAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
- [Perl](#perl)
- [PHP](#php)
- [Dart](#dart)
- [C/C++](#cc)
AryanBakliwal marked this conversation as resolved.
Show resolved Hide resolved
- [Feedback \& Contributions](#feedback--contributions)
- [Security Issues](#security-issues)

Expand Down Expand Up @@ -1520,10 +1521,16 @@

### Dart

The scanner examines the pubspec.lock file, made by Dart's pub tool for managing project dependencies and versions. The package names and versions are used to search the database for vulnerabilities.
The scanner examines the `pubspec.lock` file, made by Dart's pub tool for managing project dependencies and versions. The package names and versions are used to search the database for vulnerabilities.

Here's an example of what a [`pubspec.lock`](https://github.com/intel/cve-bin-tool/blob/main/test/language_data/pubspec.lock) file might look like.

### C/C++

The scanner examines the `conan.lock` file, generated by the Conan dependency and package manager for C and C++. The package names and versions are used to search the database for vulnerabilities.

Check warning on line 1530 in doc/MANUAL.md

View workflow job for this annotation

GitHub Actions / Spell checking

`conan` is not a recognized word. (unrecognized-spelling)

Here's an example of what a [`conan.lock`](https://github.com/intel/cve-bin-tool/blob/main/test/language_data/conan.lock) file might look like.

Check warning on line 1532 in doc/MANUAL.md

View workflow job for this annotation

GitHub Actions / Spell checking

`conan` is not a recognized word. (unrecognized-spelling)

Check warning on line 1532 in doc/MANUAL.md

View workflow job for this annotation

GitHub Actions / Spell checking

`conan` is not a recognized word. (unrecognized-spelling)

## Feedback & Contributions

Bugs and feature requests can be made via [GitHub issues](https://github.com/intel/cve-bin-tool/issues).
Expand Down
12 changes: 12 additions & 0 deletions test/language_data/conan.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "0.5",
"requires": [
"zlib/1.2.11#fca992a7d96a1b92bd956caa8a97d18f%1705999194.642",
"openssl/3.0.1w#a8f0792d7c5121b954578a7149d23e03%1717541485.78"
],
"build_requires": [
"cmake/3.22.6#f305019023c2db74d1001c5afa5cf362"
],
"python_requires": [],
"config_requires": []
}
14 changes: 14 additions & 0 deletions test/test_language_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ class TestLanguageScanner:

DART_PRODUCTS = ["dio", "archive"]

CONAN_PRODUCTS = ["zlib", "openssl", "cmake"]

@classmethod
def setup_class(cls):
cls.cvedb = CVEDB()
Expand Down Expand Up @@ -341,6 +343,18 @@ def test_language_package_none_found(self, filename: str) -> None:
)
],
),
pytest.param(
str(TEST_FILE_PATH / "conan.lock"),
parsers.ccpp.CCppParser,
CONAN_PRODUCTS,
"conan",
marks=[
pytest.mark.skipif(
not LONG_TESTS(),
reason="Test reduction in short tests",
)
],
),
],
)
def test_language_package(
Expand Down
Loading