From 7d707f7f370c31f3d9841415c1458a2d21640a15 Mon Sep 17 00:00:00 2001
From: Todd Zullinger <tmz@pobox.com>
Date: Fri, 3 Mar 2023 12:56:37 -0500
Subject: [PATCH] TagsCheck: handle license exception in grouping

A valid license exception which is in a grouping is reported as invalid.
This is due to the trailing ')' being included in the regex.  The
following license:

    (GPL-2.0-only OR GPL-3.0-only WITH Qt-GPL-exception-1.0) AND MIT

returns:

    W: invalid-license-exception Qt-GPL-exception-1.0)

Adjust the license_exception_regex to exclude trailing parenthesis.

Fixes: https://bugzilla.redhat.com/2175241
---
 rpmlint/checks/TagsCheck.py                      |   2 +-
 .../valid-exception-in-grouping-1.0-1.src.rpm    | Bin 0 -> 6615 bytes
 test/test_tags.py                                |  12 ++++++++++++
 3 files changed, 13 insertions(+), 1 deletion(-)
 create mode 100644 test/source/valid-exception-in-grouping-1.0-1.src.rpm

diff --git a/rpmlint/checks/TagsCheck.py b/rpmlint/checks/TagsCheck.py
index ad9a35f2..923538ba 100644
--- a/rpmlint/checks/TagsCheck.py
+++ b/rpmlint/checks/TagsCheck.py
@@ -21,7 +21,7 @@ lib_package_regex = re.compile(r'(?:^(?:compat-)?lib.*?(\.so.*)?|libs?[\d-]*)$',
 leading_space_regex = re.compile(r'^\s+')
 pkg_config_regex = re.compile(r'^/usr/(?:lib\d*|share)/pkgconfig/')
 license_regex = re.compile(r'\(([^)]+)\)|\s(?:and|or|AND|OR)\s')
-license_exception_regex = re.compile(r'(\S+)\s(?:WITH|with)\s(\S+)')
+license_exception_regex = re.compile(r'(\S+)\s(?:WITH|with)\s([^)]+)')
 invalid_version_regex = re.compile(r'([0-9](?:rc|alpha|beta|pre).*)', re.IGNORECASE)
 # () are here for grouping purpose in the regexp
 tag_regex = re.compile(r'^((?:Auto(?:Req|Prov|ReqProv)|Build(?:Arch(?:itectures)?|Root)|(?:Build)?Conflicts|(?:Build)?(?:Pre)?Requires|Copyright|(?:CVS|SVN)Id|Dist(?:ribution|Tag|URL)|DocDir|(?:Build)?Enhances|Epoch|Exclu(?:de|sive)(?:Arch|OS)|Group|Icon|License|Name|No(?:Patch|Source)|Obsoletes|Packager|Patch\d*|Prefix(?:es)?|Provides|(?:Build)?Recommends|Release|RHNPlatform|Serial|Source\d*|(?:Build)?Suggests|Summary|(?:Build)?Supplements|(?:Bug)?URL|Vendor|Version)(?:\([^)]+\))?:)\s*\S', re.IGNORECASE)
diff --git a/test/source/valid-exception-in-grouping-1.0-1.src.rpm b/test/source/valid-exception-in-grouping-1.0-1.src.rpm
new file mode 100644
index 0000000000000000000000000000000000000000..c74b2f048a8d45b224df143f7a77f75d21eba87c
GIT binary patch
literal 6615
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

literal 0
HcmV?d00001

diff --git a/test/test_tags.py b/test/test_tags.py
index 210fb2db..42a83154 100644
--- a/test/test_tags.py
+++ b/test/test_tags.py
@@ -77,6 +77,18 @@ def test_valid_license_exception(tmpdir, package, tagscheck):
     assert 'W: invalid-license-exception' not in out
 
 
+@pytest.mark.parametrize('package', ['source/valid-exception-in-grouping'])
+def test_valid_license_exception_in_grouping(tmpdir, package, tagscheck):
+    CONFIG.info = True
+    CONFIG.configuration['ValidLicenses'] = ['BSD-3-Clause', 'GPL-2.0-only']
+    CONFIG.configuration['ValidLicenseExceptions'] = ['Qt-GPL-exception-1.0']
+    output = Filter(CONFIG)
+    test = TagsCheck(CONFIG, output)
+    test.check(get_tested_package(package, tmpdir))
+    out = output.print_results(output.results)
+    assert 'W: invalid-license-exception' not in out
+
+
 @pytest.mark.parametrize('package', ['binary/xtables-addons-kmp-default'])
 def test_forbidden_controlchar_found_requires(tmpdir, package, tagscheck):
     output, test = tagscheck
