From 92a1fac2c106fc023a2753476b185bb4df6f3da5 Mon Sep 17 00:00:00 2001 From: Hubert Tournier Date: Sat, 13 Apr 2024 18:54:14 +0200 Subject: [PATCH] Update to v1.4.0 --- Makefile | 12 ++-- README.md | 17 ++++-- TODO.md | 1 + man/portstreelint.8 | 22 ++++--- setup.cfg | 2 +- src/portstreelint/check_licenses.py | 78 +++++++++++++++++++++++++ src/portstreelint/check_plist.py | 33 +++++++++++ src/portstreelint/check_www_site.py | 6 +- src/portstreelint/library.py | 4 ++ src/portstreelint/load_config.py | 58 +++++++++--------- src/portstreelint/load_data.py | 2 - src/portstreelint/main.py | 55 ++++++++--------- src/portstreelint/show_notifications.py | 1 - src/portstreelint/show_summary.py | 5 +- 14 files changed, 214 insertions(+), 82 deletions(-) create mode 100644 src/portstreelint/check_licenses.py diff --git a/Makefile b/Makefile index 4f6b704..48f1677 100644 --- a/Makefile +++ b/Makefile @@ -2,12 +2,12 @@ NAME=portstreelint SOURCES=src/${NAME}/__init__.py src/${NAME}/main.py src/${NAME}/library.py \ src/${NAME}/load_config.py src/${NAME}/load_data.py src/${NAME}/check_categories.py \ src/${NAME}/check_comment.py src/${NAME}/check_description_file.py \ - src/${NAME}/check_installation_prefix.py src/${NAME}/check_maintainer.py \ - src/${NAME}/check_marks.py src/${NAME}/check_plist.py src/${NAME}/check_port_path.py \ - src/${NAME}/check_unchanging_ports.py src/${NAME}/check_vulnerabilities.py \ - src/${NAME}/check_www_site.py src/${NAME}/show_categories.py \ - src/${NAME}/show_maintainers.py src/${NAME}/show_notifications.py \ - src/${NAME}/show_summary.py + src/${NAME}/check_installation_prefix.py src/${NAME}/check_licenses.py \ + src/${NAME}/check_maintainer.py src/${NAME}/check_marks.py src/${NAME}/check_plist.py \ + src/${NAME}/check_port_path.py src/${NAME}/check_unchanging_ports.py \ + src/${NAME}/check_vulnerabilities.py src/${NAME}/check_www_site.py \ + src/${NAME}/show_categories.py src/${NAME}/show_maintainers.py \ + src/${NAME}/show_notifications.py src/${NAME}/show_summary.py # Default action is to show this help message: .help: diff --git a/README.md b/README.md index 40d440a..401a5da 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ portstreelint - FreeBSD ports tree lint ## SYNOPSIS **portstreelint** -\[--nocfg|-n\] +\[--nocfg\] \[--gencfg|-g FILE\] \[--show-cat|-C\] \[--show-mnt|-M\] @@ -98,6 +98,10 @@ The checks list includes: * Ports unchanged for a long time (info) * Makefile:PORTVERSION and Makefile:DISTVERSION used simultaneously * VuXML vulnerabilities for the current port versions (warning) +* Missing Makefile:LICENSE +* Unofficial licenses (warning) +* Unnecessary Makefile:LICENSE_COMB=single (warning) +* Unnecessary Makefile:LICENSE_COMB=multi (warning) It's possible to change the default values for PLIST_FILES abuse, BROKEN_since, DEPRECATED_since, FORBIDDEN_since and Unchanged_since @@ -115,16 +119,17 @@ since previous run... For convenience, you can put your favourite options in a configuration file, which will be read before processing the -environment and the command line, unless you use the *--nocfg|-n* +environment and the command line, unless you use the *--nocfg* option. You can generate a default configuration file with the *--gencfg|-g* option followed by a filename. This file also offers full control over the checks to perform, and a way to discard -false-positive vulnerabilities. +false-positive vulnerabilities and unwanted licenses report +messages. ### OPTIONS Options | Use ------- | --- ---nocfg\|-n|Don't use the configuration file +--nocfg|Don't use the configuration file --gencfg\|-g FILE|Generate a default configuration file in FILE --show-cat\|-C|Show categories with ports count --show-mnt\|-M|Show maintainers with ports count @@ -230,8 +235,8 @@ The IGNORE mark check is not reliable because this tool doesn't parse the ports' Makefiles, but just loads their variables without regard to the conditional tests that may surround them. -The nonexistent plist is not very helpful because there are unaccounted -autoplist options for some languages (Python)... +The nonexistent plist check is not very helpful because there are still +undocumented cases where the list is auto generated. The ports using exotic versioning schemes will be skipped from the vulnerability check because the library we use for version comparisons diff --git a/TODO.md b/TODO.md index d72c81d..3ba6d49 100644 --- a/TODO.md +++ b/TODO.md @@ -8,6 +8,7 @@ Feel free to submit your own ideas! ## Probable evolutions * Checking distfiles availability * Improving versions comparison for versions with letters -> pnu-vuxml change needed +* More exclusions loaded from the config file ## Possible evolutions * Printing the number of notifications and a congratulation message if everything is OK diff --git a/man/portstreelint.8 b/man/portstreelint.8 index bb4e1c7..b02977d 100644 --- a/man/portstreelint.8 +++ b/man/portstreelint.8 @@ -1,4 +1,4 @@ -.Dd April 1st, 2024 +.Dd April 13, 2024 .Dt portstreelint 8 .Os .Sh NAME @@ -6,7 +6,7 @@ .Nd FreeBSD ports tree lint .Sh SYNOPSIS .Nm -.Op Fl \-nocfg|\-n +.Op Fl \-nocfg .Op Fl \-gencfg|\-g Ar FILE .Op Fl \-show\-cat|\-C .Op Fl \-show\-mnt|\-M @@ -130,6 +130,14 @@ Ports unchanged for a long time (info) Makefile:PORTVERSION and Makefile:DISTVERSION used simultaneously .It VuXML vulnerabilities for the current port versions (warning) +.It +Missing Makefile:LICENSE +.It +Unofficial licenses (warning) +.It +Unnecessary Makefile:LICENSE_COMB=single (warning) +.It +Unnecessary Makefile:LICENSE_COMB=multi (warning) .El .Pp It's possible to change the default values for PLIST_FILES abuse, @@ -159,14 +167,14 @@ results and displaying only diffs since previous run... For convenience, you can put your favourite options in a configuration file, which will be read before processing the environment and the command line, unless you use the -.Op Fl \-nocfg|\-n +.Op Fl \-nocfg option. You can generate a default configuration file with the .Op Fl \-gencfg|\-g option followed by a filename. This file also offers full control over the checks to perform, and a way to discard false\-positive -vulnerabilities. +vulnerabilities and unwanted licenses report messages. .Ss OPTIONS -.Op Fl \-nocfg|\-n +.Op Fl \-nocfg Don't use the configuration file .Pp .Op Fl \-gencfg|\-g Ar FILE @@ -337,8 +345,8 @@ The IGNORE mark check is not reliable because this tool doesn't parse the ports' Makefiles, but just loads their variables without regard to the conditional tests that may surround them. .Pp -The nonexistent plist is not very helpful because there are -unaccounted autoplist options for some languages (Python)... +The nonexistent plist check is not very helpful because there are still +undocumented cases where the list is auto generated. .Pp The ports using exotic versioning schemes will be skipped from the vulnerability check because the library we use for version comparisons diff --git a/setup.cfg b/setup.cfg index 5532ddc..a648465 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,7 +3,7 @@ name = pnu-portstreelint description = FreeBSD ports tree lint long_description = file: README.md long_description_content_type = text/markdown -version = 1.3.0 +version = 1.4.0 license = BSD 3-Clause License license_files = License author = Hubert Tournier diff --git a/src/portstreelint/check_licenses.py b/src/portstreelint/check_licenses.py new file mode 100644 index 0000000..f0143eb --- /dev/null +++ b/src/portstreelint/check_licenses.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +""" portstreelint - FreeBSD ports tree lint +License: 3-clause BSD (see https://opensource.org/licenses/BSD-3-Clause) +Author: Hubert Tournier +""" + +import logging + +from .library import counters, notify_maintainer + +OFFICIAL_LICENSES = [ + "AGPLv3", "AGPLv3+", "APACHE10", "APACHE11", "APACHE20", "ART10", "ART20", "ARTPERL10", "BSD", + "BSD0CLAUSE", "BSD2CLAUSE", "BSD3CLAUSE", "BSD4CLAUSE", "BSL", "CC-BY-1.0", "CC-BY-2.0", + "CC-BY-2.5", "CC-BY-3.0", "CC-BY-4.0", "CC-BY-NC-1.0", "CC-BY-NC-2.0", "CC-BY-NC-2.5", + "CC-BY-NC-3.0", "CC-BY-NC-4.0", "CC-BY-NC-ND-1.0", "CC-BY-NC-ND-2.0", "CC-BY-NC-ND-2.5", + "CC-BY-NC-ND-3.0", "CC-BY-NC-ND-4.0", "CC-BY-NC-SA-1.0", "CC-BY-NC-SA-2.0", "CC-BY-NC-SA-2.5", + "CC-BY-NC-SA-3.0", "CC-BY-NC-SA-4.0", "CC-BY-ND-1.0", "CC-BY-ND-2.0", "CC-BY-ND-2.5", + "CC-BY-ND-3.0", "CC-BY-ND-4.0", "CC-BY-SA-1.0", "CC-BY-SA-2.0", "CC-BY-SA-2.5", "CC-BY-SA-3.0", + "CC-BY-SA-4.0", "CC0-1.0", "CDDL", "ClArtistic", "CPAL-1.0", "EPL", "GFDL", "GMGPL", "GPLv1", + "GPLv1+", "GPLv2", "GPLv2+", "GPLv3", "GPLv3+", "GPLv3RLE", "GPLv3RLE+", "ISCL", "LGPL20", + "LGPL20+", "LGPL21", "LGPL21+", "LGPL3", "LGPL3+", "LPPL10", "LPPL11", "LPPL12", "LPPL13", + "LPPL13a", "LPPL13b", "LPPL13c", "MIT", "MPL10", "MPL11", "MPL20", "NCSA", "NONE", "ODbL", + "OFL10", "OFL11", "OpenSSL", "OWL", "PD", "PHP202", "PHP30", "PHP301", "PostgreSQL", "PSFL", + "RUBY", "UNLICENSE", "WTFPL", "WTFPL1", "ZLIB", "ZPL21", +] + +#################################################################################################### +def _debug_license(name, port): + """ """ + print(f"===== {name} =====") + for key, value in port.items(): + if key.startswith("LICENSE"): + print(f"{key}={value}") + print(f"======{'=' * len(name)}======\n") + +#################################################################################################### +def check_licenses(ports, excluded_licenses): + """ Cross-checks the licenses fields with the Makefile and compliance with rules + Rules at https://docs.freebsd.org/en/books/porters-handbook/makefiles/#makefile-categories-definition + """ + for name, port in ports.items(): + if "PORTNAME" in port: + if port["PORTNAME"] in excluded_licenses: + # Let's skip this port... + continue + + if "LICENSE" not in port: + logging.error("Missing LICENSE in Makefile for port %s", name) + counters["Missing LICENSE"] += 1 + notify_maintainer(port["maintainer"], "Missing LICENSE", name) + else: + for license_name in port["LICENSE"].split(): + if license_name not in OFFICIAL_LICENSES and '$' not in license_name: + logging.warning("Unofficial license '%s' in Makefile for port %s", license_name, name) + counters["Unofficial licenses"] += 1 + notify_maintainer(port["maintainer"], "Unofficial license", name) + + if "LICENSE_COMB" in port: + if port["LICENSE_COMB"] == "single": + logging.warning("Unnecessary LICENSE_COMB=single in Makefile for port %s", name) + counters["Unnecessary LICENSE_COMB=single"] += 1 + notify_maintainer(port["maintainer"], "Unnecessary LICENSE_COMB=single", name) + elif port["LICENSE_COMB"] not in ("multi", "dual"): + logging.error("Unknown LICENSE_COMB value '%s' in Makefile for port %s (not counted)", port["LICENSE_COMB"], name) + elif len(port["LICENSE"].split()) == 1: + for key in port: + if key.startswith("LICENSE_NAME_"): + # It's OK, there are additional licenses defined with LICENSE_NAME_* entries + continue + + logging.warning("Unnecessary LICENSE_COMB=%s in Makefile for port %s", port["LICENSE_COMB"], name) + counters["Unnecessary LICENSE_COMB=multi"] += 1 + notify_maintainer(port["maintainer"], "Unnecessary LICENSE_COMB=multi", name) + + logging.info("Found %d ports with missing LICENSE", counters["Missing LICENSE"]) + logging.info("Found %d ports with unofficial licenses", counters["Unofficial licenses"]) + logging.info("Found %d ports with unnecessary LICENSE_COMB=single", counters["Unnecessary LICENSE_COMB=single"]) + logging.info("Found %d ports with unnecessary LICENSE_COMB=multi", counters["Unnecessary LICENSE_COMB=multi"]) diff --git a/src/portstreelint/check_plist.py b/src/portstreelint/check_plist.py index 57dd7eb..b47be38 100644 --- a/src/portstreelint/check_plist.py +++ b/src/portstreelint/check_plist.py @@ -21,6 +21,39 @@ def check_plist(ports, plist_abuse, ports_dir): if os.path.isdir(port_path): if not os.path.isfile(port_path + os.sep + "pkg-plist"): if not "PLIST_FILES" in port: + if "AP_GENPLIST" in port: + continue + + if "USES" in port: + items = port["USES"].split() + + found = False + for item in items: + if item in ("pear", "horde", "gem"): + found = True + break + + if item.startswith("pear:") \ + or item.startswith("horde:") \ + or item.startswith("gem:"): + found = True + break + + if item.startswith("cran:"): + if "auto-plist" in item.split(':')[1].split(','): + found = True + break + + if found: + continue + + if "USE_PYTHON" in port: + if "autoplist" in port["USE_PYTHON"].split(): + continue + + if "RUBYGEM_AUTOPLIST" in port or "RUBYGEM_AUTOPLIST_ALT" in port: + continue + if not "PLIST" in port and not "PLIST_SUB" in port: logging.debug("Nonexistent pkg-plist/PLIST_FILES/PLIST/PLIST_SUB for port %s", name) counters["Nonexistent pkg-plist"] += 1 diff --git a/src/portstreelint/check_www_site.py b/src/portstreelint/check_www_site.py index 3e9ffa8..3173c64 100644 --- a/src/portstreelint/check_www_site.py +++ b/src/portstreelint/check_www_site.py @@ -14,8 +14,8 @@ # Headers and timeout delay for HTTP(S) requests: HTTP_HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", - "Accept-Language": "en;q=1.0, en-US;q=0.8, *;q=0.5", + "Accept": "*/*", + "Accept-Language": "en;q=1.0, *;q=0.5", "Accept-Encoding": "identity", "Connection": "keep-alive", } @@ -32,7 +32,6 @@ def _resolve_hostname(hostname): return ip_address - #################################################################################################### def _handle_url_errors(port_name, www_site, error, maintainer): """ Decides what to do with the multiple possible fetching errors """ @@ -85,7 +84,6 @@ def _handle_url_errors(port_name, www_site, error, maintainer): return is_unaccessible - #################################################################################################### def check_www_site(ports, check_host, check_url): """ Checks the www-site field existence diff --git a/src/portstreelint/library.py b/src/portstreelint/library.py index 2046e61..38fe892 100644 --- a/src/portstreelint/library.py +++ b/src/portstreelint/library.py @@ -41,6 +41,10 @@ "Both PORTVERSION and DISTVERSION": 0, "Vulnerable port version": 0, "Skipped vulnerability checks": 0, + "Missing LICENSE": 0, + "Unofficial licenses": 0, + "Unnecessary LICENSE_COMB=single": 0, + "Unnecessary LICENSE_COMB=multi": 0, } # Global dictionary of notifications to port maintainers: diff --git a/src/portstreelint/load_config.py b/src/portstreelint/load_config.py index 3dddf4a..9d1c34a 100644 --- a/src/portstreelint/load_config.py +++ b/src/portstreelint/load_config.py @@ -10,7 +10,6 @@ import libpnu - #################################################################################################### def generate_config(parameters): """ Generates a configuration file with the default + environment + command line parameters """ @@ -28,19 +27,20 @@ def generate_config(parameters): #debug_level = debug [checks] -port_path = {parameters['Checks']['port-path']} -installation_prefix = {parameters['Checks']['installation-prefix']} +categories = {parameters['Checks']['categories']} comment = {parameters['Checks']['comment']} description_file = {parameters['Checks']['description-file']} -plist = {parameters['Checks']['plist']} -maintainer = {parameters['Checks']['maintainer']} -categories = {parameters['Checks']['categories']} -www_site = {parameters['Checks']['www-site']} hostnames = {parameters['Checks']['Hostnames']} -url = {parameters['Checks']['URL']} +installation_prefix = {parameters['Checks']['installation-prefix']} +licenses = {parameters['Checks']['Licenses']} +maintainer = {parameters['Checks']['maintainer']} marks = {parameters['Checks']['Marks']} +plist = {parameters['Checks']['plist']} +port_path = {parameters['Checks']['port-path']} unchanging_ports = {parameters['Checks']['Unchanging ports']} +url = {parameters['Checks']['URL']} vulnerabilities = {parameters['Checks']['Vulnerabilities']} +www_site = {parameters['Checks']['www-site']} [limits] # number of entries: @@ -58,14 +58,16 @@ def generate_config(parameters): ports = [exclusions] -# (multilines) lists of space separated Vulnerabilities IDs: -vulnerabilities = +# (multilines) list of space separated Vulnerabilities IDs: +vulnerabilities = 92442c4b-6f4a-11db-bd28-0012f06707f0 + bd579366-5290-11d9-ac20-00065be4b5b6 +# (multilines) list of space separated PORTNAMEs: +licenses = xfce """ with open(parameters["INI filename"], "w", encoding="utf-8") as file: file.write(content) - #################################################################################################### def _string2bool(string): """ Converts a string to a boolean without ValueError exception """ @@ -75,7 +77,6 @@ def _string2bool(string): # .getboolean() would generate an exception if string wasn't in "false", "no", "off", 0"... return False - #################################################################################################### def _string2int(string, default): """ Converts a string to an int or use a default value without ValueError exception """ @@ -86,7 +87,6 @@ def _string2int(string, default): return value - #################################################################################################### def load_config(parameters): """ Loads the user configuration file """ @@ -110,32 +110,34 @@ def load_config(parameters): elif config["params"]["debug_level"] == "debug": logging.disable(logging.NOTSET) if "checks" in config: - if "port_path" in config["checks"]: - parameters["Checks"]["port-path"] = _string2bool(config["checks"]["port_path"]) - if "installation_prefix" in config["checks"]: - parameters["Checks"]["installation-prefix"] = _string2bool(config["checks"]["installation_prefix"]) + if "categories" in config["checks"]: + parameters["Checks"]["categories"] = _string2bool(config["checks"]["categories"]) if "comment" in config["checks"]: parameters["Checks"]["comment"] = _string2bool(config["checks"]["comment"]) if "description_file" in config["checks"]: parameters["Checks"]["description-file"] = _string2bool(config["checks"]["description_file"]) - if "plist" in config["checks"]: - parameters["Checks"]["plist"] = _string2bool(config["checks"]["plist"]) - if "maintainer" in config["checks"]: - parameters["Checks"]["maintainer"] = _string2bool(config["checks"]["maintainer"]) - if "categories" in config["checks"]: - parameters["Checks"]["categories"] = _string2bool(config["checks"]["categories"]) - if "www_site" in config["checks"]: - parameters["Checks"]["www-site"] = _string2bool(config["checks"]["www_site"]) if "hostnames" in config["checks"]: parameters["Checks"]["Hostnames"] = _string2bool(config["checks"]["hostnames"]) - if "url" in config["checks"]: - parameters["Checks"]["URL"] = _string2bool(config["checks"]["url"]) + if "installation_prefix" in config["checks"]: + parameters["Checks"]["installation-prefix"] = _string2bool(config["checks"]["installation_prefix"]) + if "licenses" in config["checks"]: + parameters["Checks"]["Licenses"] = _string2bool(config["checks"]["licenses"]) + if "maintainer" in config["checks"]: + parameters["Checks"]["maintainer"] = _string2bool(config["checks"]["maintainer"]) if "marks" in config["checks"]: parameters["Checks"]["Marks"] = _string2bool(config["checks"]["marks"]) + if "plist" in config["checks"]: + parameters["Checks"]["plist"] = _string2bool(config["checks"]["plist"]) + if "port_path" in config["checks"]: + parameters["Checks"]["port-path"] = _string2bool(config["checks"]["port_path"]) if "unchanging_ports" in config["checks"]: parameters["Checks"]["Unchanging ports"] = _string2bool(config["checks"]["unchanging_ports"]) + if "url" in config["checks"]: + parameters["Checks"]["URL"] = _string2bool(config["checks"]["url"]) if "vulnerabilities" in config["checks"]: parameters["Checks"]["Vulnerabilities"] = _string2bool(config["checks"]["vulnerabilities"]) + if "www_site" in config["checks"]: + parameters["Checks"]["www-site"] = _string2bool(config["checks"]["www_site"]) if "limits" in config: if "plist_abuse" in config["limits"]: parameters["Limits"]["PLIST abuse"] = _string2int(config["limits"]["plist_abuse"], parameters["Limits"]["PLIST abuse"]) @@ -157,5 +159,7 @@ def load_config(parameters): if "exclusions" in config: if "vulnerabilities" in config["exclusions"]: parameters["Exclusions"]["Vulnerabilities"] = config["exclusions"]["vulnerabilities"].split() + if "licenses" in config["exclusions"]: + parameters["Exclusions"]["Licenses"] = config["exclusions"]["licenses"].split() return parameters diff --git a/src/portstreelint/load_data.py b/src/portstreelint/load_data.py index 1665bdc..cf5dcc9 100644 --- a/src/portstreelint/load_data.py +++ b/src/portstreelint/load_data.py @@ -64,7 +64,6 @@ def load_freebsd_ports_dict(ports_dir): logging.info("Loaded %d ports from the FreeBSD Ports INDEX file", len(ports)) return ports - #################################################################################################### def filter_ports(ports, selected_categories, selected_maintainers, selected_ports): """ Filters the list of ports to the specified categories AND maintainers""" @@ -93,7 +92,6 @@ def filter_ports(ports, selected_categories, selected_maintainers, selected_port logging.info("Selected %d ports", len(ports)) return ports - #################################################################################################### def update_with_makefiles(ports, ports_dir): """ Loads selected part of port's Makefiles for cross-checking things """ diff --git a/src/portstreelint/main.py b/src/portstreelint/main.py index 6cc8af4..3ed3467 100644 --- a/src/portstreelint/main.py +++ b/src/portstreelint/main.py @@ -12,31 +12,30 @@ import libpnu -from .load_data import load_freebsd_ports_dict, filter_ports, update_with_makefiles from .load_config import generate_config, load_config -from .check_port_path import check_port_path -from .check_installation_prefix import check_installation_prefix +from .load_data import load_freebsd_ports_dict, filter_ports, update_with_makefiles +from .check_categories import check_categories from .check_comment import check_comment from .check_description_file import check_description_file -from .check_plist import check_plist +from .check_installation_prefix import check_installation_prefix +from .check_licenses import check_licenses from .check_maintainer import check_maintainer -from .check_categories import check_categories -from .check_www_site import check_www_site from .check_marks import check_marks +from .check_plist import check_plist +from .check_port_path import check_port_path from .check_unchanging_ports import check_unchanging_ports from .check_vulnerabilities import check_vulnerabilities +from .check_www_site import check_www_site from .show_categories import show_categories from .show_maintainers import show_maintainers from .show_notifications import show_notifications, output_notifications from .show_summary import show_summary - # Version string used by the what(1) and ident(1) commands: -ID = "@(#) $Id: portstreelint - FreeBSD ports tree lint v1.3.0 (April 1st, 2024) by Hubert Tournier $" +ID = "@(#) $Id: portstreelint - FreeBSD ports tree lint v1.4.0 (April 13, 2024) by Hubert Tournier $" # Default parameters. Can be overcome by command line options: parameters = { - "Load config": True, "INI filename": "", "Ports dir": "/usr/ports", "CSV filename": "", @@ -44,19 +43,20 @@ "Show maintainers": False, "Checks": { - "port-path": True, - "installation-prefix": True, + "categories": True, "comment": True, "description-file": True, - "plist": True, - "maintainer": True, - "categories": True, - "www-site": True, "Hostnames": False, - "URL": False, + "installation-prefix": True, + "Licenses": True, + "maintainer": True, "Marks": True, + "plist": True, + "port-path": True, "Unchanging ports": True, + "URL": False, "Vulnerabilities": True, + "www-site": True, }, "Limits": { "PLIST abuse": 7, # entries @@ -72,22 +72,22 @@ }, "Exclusions": { "Vulnerabilities": [], + "Licenses": [], }, } - #################################################################################################### def _display_help(): """ Display usage and help """ #pylint: disable=C0301 - print("usage: portstreelint [--nocfg|-n] [--gencfg|-g FILE]", file=sys.stderr) + print("usage: portstreelint [--nocfg] [--gencfg|-g FILE]", file=sys.stderr) print(" [--tree|-t DIR] [--show-cat|-C] [--show-mnt|-M]", file=sys.stderr) print(" [--cat|-c LIST] [--mnt|-m LIST] [--port|-p LIST] [--plist NUM]", file=sys.stderr) print(" [--broken NUM] [--deprecated NUM] [--forbidden NUM] [--unchanged NUM]", file=sys.stderr) print(" [--check-host|-h] [--check-url|-u] [--output|-o FILE]", file=sys.stderr) print(" [--debug] [--help|-?] [--info] [--version] [--]", file=sys.stderr) print(" ------------------ -------------------------------------------------------", file=sys.stderr) - print(" --nocfg|-n Don't use the configuration file", file=sys.stderr) + print(" --nocfg Don't use the configuration file", file=sys.stderr) print(" --gencfg|-g FILE Generate a default configuration file in FILE", file=sys.stderr) print(" --show-cat|-C Show categories with ports count", file=sys.stderr) print(" --show-mnt|-M Show maintainers with ports count", file=sys.stderr) @@ -111,7 +111,6 @@ def _display_help(): print(file=sys.stderr) #pylint: enable=C0301 - #################################################################################################### def _process_environment_variables(): """ Process environment variables """ @@ -131,7 +130,6 @@ def _process_environment_variables(): logging.debug("_process_environment_variables(): parameters:") logging.debug(parameters) - #################################################################################################### def _process_command_line(): """ Process command line options """ @@ -141,7 +139,7 @@ def _process_command_line(): # option letters followed by : expect an argument # same for option strings followed by = - character_options = "CMc:g:hm:no:p:t:u?" + character_options = "CMc:g:hm:o:p:t:u?" string_options = [ "broken=", "cat=", @@ -240,8 +238,8 @@ def _process_command_line(): maintainers = argument.lower().split(",") parameters["Selections"]["Maintainers"] = [m if '@' in m else f"{m}@freebsd.org" for m in maintainers] - elif option in ("--nocfg", "-n"): - parameters["Load config"] = False + elif option == "--nocfg": + pass # need to be handled BEFORE this function elif option in ("--output", "-o"): parameters["CSV filename"] = argument @@ -294,7 +292,6 @@ def _process_command_line(): return remaining_arguments - #################################################################################################### def main(): """ The program's main entry point """ @@ -307,7 +304,8 @@ def main(): libpnu.initialize_debugging(program_name) libpnu.handle_interrupt_signals(libpnu.interrupt_handler_function) - if parameters["Load config"]: + print(sys.argv[1:]) + if not "--nocfg" in sys.argv[1:]: parameters = load_config(parameters) _process_environment_variables() _ = _process_command_line() @@ -390,6 +388,10 @@ def main(): if parameters["Checks"]["Vulnerabilities"]: check_vulnerabilities(ports, parameters["Exclusions"]["Vulnerabilities"]) + # Check licenses + if parameters["Checks"]["Licenses"]: + check_licenses(ports, parameters["Exclusions"]["Licenses"]) + # Print results per maintainer show_notifications() @@ -402,6 +404,5 @@ def main(): sys.exit(0) - if __name__ == "__main__": main() diff --git a/src/portstreelint/show_notifications.py b/src/portstreelint/show_notifications.py index 3be0064..5efce11 100644 --- a/src/portstreelint/show_notifications.py +++ b/src/portstreelint/show_notifications.py @@ -25,7 +25,6 @@ def show_notifications(): print(f" {line}") print() - #################################################################################################### def output_notifications(filename): """ Output notifications in a CSV file """ diff --git a/src/portstreelint/show_summary.py b/src/portstreelint/show_summary.py index 4d80b61..787afe9 100644 --- a/src/portstreelint/show_summary.py +++ b/src/portstreelint/show_summary.py @@ -13,7 +13,6 @@ def _conditional_print(counter, message): value = counters[counter] print(f" {value} port{'' if value == 1 else 's'} {message}") - #################################################################################################### def show_summary(limits): """ Pretty prints a summary of findings """ @@ -50,3 +49,7 @@ def show_summary(limits): _conditional_print("Unchanged for a long time", f"with a last modification older than {limits['Unchanged since']} days (info)") _conditional_print("Both PORTVERSION and DISTVERSION", "with both PORTVERSION and DISTVERSION") _conditional_print("Vulnerable port version", "with a vulnerable version (warning)") + _conditional_print("Missing LICENSE", "without defined LICENSE") + _conditional_print("Unofficial licenses", "referring to unofficial licenses (warning)") + _conditional_print("Unnecessary LICENSE_COMB=single", "with unnecessary LICENSE_COMB=single (warning)") + _conditional_print("Unnecessary LICENSE_COMB=multi", "with unnecessary LICENSE_COMB=multi (warning)")