Skip to content

Commit

Permalink
feat: perl language parser (intel#2614)
Browse files Browse the repository at this point in the history
* fixes intel#1978
  • Loading branch information
Rexbeast2 authored Apr 18, 2023
1 parent fa05ece commit 54da5b0
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/actions/spelling/allow.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ connman
conventionalcommits
copyleft
coreinfrastructure
cpanfile
cpio
cpp
CQA
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ For more details, see our [documentation](https://cve-bin-tool.readthedocs.io/en
- [Go](#go)
- [Swift](#swift)
- [Python](#python)
- [Perl](#perl)
- [SBOM Generation](#sbom-generation)
- [Limitations](#limitations)
- [Additional Requirements](#additional-requirements)
Expand Down Expand Up @@ -520,6 +521,14 @@ The tool supports the scanning of the contents of any Wheel package files (indic

The `--package-list` option can be used with a Python dependencies file `requirements.txt` to find the vulnerabilities in the list of components.

### Perl

The scanner examines the `cpanfile` file within a perl application to identify components. The package names and versions are used to search the database for vulnerabilities.

The `cpanfile` must specify the version data for the vulnerability scanner to work. At this time packages without versions will be ignored. (Patches welcome to improve this behaviour in future!)

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

## SBOM Generation

To generate a software bill of materials file (SBOM) ensure these options are included:
Expand Down
1 change: 1 addition & 0 deletions cve_bin_tool/parsers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"go",
"swift",
"php",
"perl",
]


Expand Down
2 changes: 2 additions & 0 deletions cve_bin_tool/parsers/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from cve_bin_tool.parsers.go import GoParser
from cve_bin_tool.parsers.java import JavaParser
from cve_bin_tool.parsers.javascript import JavascriptParser
from cve_bin_tool.parsers.perl import PerlParser
from cve_bin_tool.parsers.php import PhpParser
from cve_bin_tool.parsers.python import PythonParser, PythonRequirementsParser
from cve_bin_tool.parsers.r import RParser
Expand All @@ -23,6 +24,7 @@
"Gemfile.lock": RubyParser,
"Package.resolved": SwiftParser,
"composer.lock": PhpParser,
"cpanfile": PerlParser,
}


Expand Down
35 changes: 35 additions & 0 deletions cve_bin_tool/parsers/perl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright (C) 2022 Intel Corporation
# SPDX-License-Identifier: GPL-3.0-or-later

from cve_bin_tool.parsers import Parser


class PerlParser(Parser):
def __init__(self, cve_db, logger):
super().__init__(cve_db, logger)

def run_checker(self, filename):
self.filename = filename
with open(self.filename) as fh:
data = fh.readlines()
# Split each line into tokens and find dependencies
dependencies = []
for line in data:
tokens = line.split()
if len(tokens) >= 4 and tokens[0] == "requires" and tokens[2] == "=>":
dependencies.append(
(
tokens[1].strip('"').split("::")[-1],
tokens[3].strip("'").strip(";").strip('"'),
)
)
elif len(tokens) >= 1 and tokens[0] == "requires":
name = (tokens[1].strip('"').split("::")[-1],)
self.logger.debug(f"Dependency with no version information: {name}")

# Print the extracted dependencies
for dependency in dependencies:
vendor = self.find_vendor(dependency[0], dependency[1])
if vendor is not None:
yield from vendor
self.logger.debug(f"Done scanning file: {self.filename}")
35 changes: 35 additions & 0 deletions test/language_data/cpanfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use strict;
use warnings;

requires "Carp" => "0";
requires "HTTP::Tiny" => "0.056";
requires "IO::Socket::SSL" => "1.42";
requires "JSON::MaybeXS" => "0";
requires "JSON::PP" => "0";
requires "Moo" => "0";
requires "Moo::Role" => "0";
requires "Net::SSLeay" => "1.49";
requires "Ref::Util" => "0";
requires "Safe::Isa" => "0";
requires "Type::Tiny" => "0";
requires "URI::Escape";
requires "perl" => "5.010";
requires "strict" => "0";
requires "warnings" => "0";

on 'test' => sub {
requires "Test::Fatal" => "0";
requires "Test::More" => "0";
requires "Test::Needs" => "0.002005";
requires "base" => "0";
requires "blib" => "1.01";
requires "LWP::Protocol::https" => "0";
recommends "HTTP::Tiny::Mech" => "1.001002";
recommends "WWW::Mechanize::Cached" => "1.54";
};

on 'develop' => sub {
requires "HTTP::Tiny::Mech" => "1.001002";
requires "LWP::Protocol::https" => "0";
requires "WWW::Mechanize::Cached" => "1.54";
};
2 changes: 2 additions & 0 deletions test/test_language_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ class TestLanguageScanner:
"database",
"phpunit",
]
PERL_PRODUCTS = ["perl", "warnings", "base"]

SWIFT_PRODUCTS = ["alliance_web_platform"]

Expand Down Expand Up @@ -213,6 +214,7 @@ def test_language_package_none_found(self, filename: str) -> None:
(str(TEST_FILE_PATH / "go.mod"), GO_PRODUCTS),
(str(TEST_FILE_PATH / "Package.resolved"), SWIFT_PRODUCTS),
(str(TEST_FILE_PATH / "composer.lock"), PHP_PRODUCTS),
(str(TEST_FILE_PATH / "cpanfile"), PERL_PRODUCTS),
],
)
def test_language_package(self, filename: str, products) -> None:
Expand Down

0 comments on commit 54da5b0

Please sign in to comment.