Merge branch 'license-overhaul-2' into 'master'

license overhaul: require FSF or OSI approved license + add override option for default license white-list

See merge request fdroid/fdroidserver!682
This commit is contained in:
Hans-Christoph Steiner 2019-10-03 22:01:07 +00:00
commit 17f37414db
4 changed files with 292 additions and 353 deletions

View file

@ -330,3 +330,20 @@ The repository of older versions of applications from the main demo repository.
# 'com.facebook.orca',
# 'com.android.vending',
# )
# `fdroid lint` checks licenses in metadata against a built white list. By
# default we will require license metadata to be present and only allow
# licenses approved either by FSF or OSI. We're using the standardized SPDX
# license IDs. (https://spdx.org/licenses/)
#
# We use `python3 -m spdx-license-list print --filter-fsf-or-osi` for
# generating our default list. (https://pypi.org/project/spdx-license-list)
#
# You can override our default list of allowed licenes by setting this option.
# Just supply a custom list of licene names you would like to allow. Setting
# this to `None` disables this lint check.
#
# lint_licenses = (
# 'Custom-License-A',
# 'Another-License',
# )

View file

@ -57,6 +57,7 @@ from pyasn1_modules import rfc2315
from pyasn1.error import PyAsn1Error
import fdroidserver.metadata
import fdroidserver.lint
from fdroidserver import _
from fdroidserver.exception import FDroidException, VCSException, NoSubmodulesException,\
BuildException, VerificationException
@ -145,6 +146,7 @@ default_config = {
using the tools on https://gitlab.com/u/fdroid.
''',
'archive_older': 0,
'lint_licenses': fdroidserver.lint.APPROVED_LICENSES,
}

View file

@ -440,10 +440,17 @@ def check_format(app):
def check_license_tag(app):
'''Ensure all license tags are in https://spdx.org/license-list'''
if app.License.rstrip('+') not in SPDX:
yield _('Invalid license tag "%s"! Use only tags from https://spdx.org/license-list') \
% (app.License)
'''Ensure all license tags contain only valid/approved values'''
if config['lint_licenses'] is None:
return
if app.License not in config['lint_licenses']:
if config['lint_licenses'] == APPROVED_LICENSES:
yield _('Unexpected license tag "{}"! Only use FSF or OSI '
'approved tags from https://spdx.org/license-list') \
.format(app.License)
else:
yield _('Unexpected license tag "{}"! Only use license tags '
'configured in your config file').format(app.License)
def check_extlib_dir(apps):
@ -622,356 +629,175 @@ def main():
sys.exit(1)
# A compiled, public domain list of official SPDX license tags from:
# https://github.com/sindresorhus/spdx-license-list/blob/v4.0.0/spdx-simple.json
# The deprecated license tags have been removed from the list, they are at the
# bottom, starting after the last license tags that start with Z.
# This is at the bottom, since its a long list of data
SPDX = [
"PublicDomain", # an F-Droid addition, until we can enforce a better option
"0BSD",
"AAL",
"Abstyles",
"Adobe-2006",
"Adobe-Glyph",
"ADSL",
"AFL-1.1",
"AFL-1.2",
"AFL-2.0",
"AFL-2.1",
"AFL-3.0",
"Afmparse",
"AGPL-1.0",
"AGPL-3.0-only",
"AGPL-3.0-or-later",
"Aladdin",
"AMDPLPA",
"AML",
"AMPAS",
"ANTLR-PD",
"Apache-1.0",
"Apache-1.1",
"Apache-2.0",
"APAFML",
"APL-1.0",
"APSL-1.0",
"APSL-1.1",
"APSL-1.2",
"APSL-2.0",
"Artistic-1.0-cl8",
"Artistic-1.0-Perl",
"Artistic-1.0",
"Artistic-2.0",
"Bahyph",
"Barr",
"Beerware",
"BitTorrent-1.0",
"BitTorrent-1.1",
"Borceux",
"BSD-1-Clause",
"BSD-2-Clause-FreeBSD",
"BSD-2-Clause-NetBSD",
"BSD-2-Clause-Patent",
"BSD-2-Clause",
"BSD-3-Clause-Attribution",
"BSD-3-Clause-Clear",
"BSD-3-Clause-LBNL",
"BSD-3-Clause-No-Nuclear-License-2014",
"BSD-3-Clause-No-Nuclear-License",
"BSD-3-Clause-No-Nuclear-Warranty",
"BSD-3-Clause",
"BSD-4-Clause-UC",
"BSD-4-Clause",
"BSD-Protection",
"BSD-Source-Code",
"BSL-1.0",
"bzip2-1.0.5",
"bzip2-1.0.6",
"Caldera",
"CATOSL-1.1",
"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-1.0",
"CDDL-1.1",
"CDLA-Permissive-1.0",
"CDLA-Sharing-1.0",
"CECILL-1.0",
"CECILL-1.1",
"CECILL-2.0",
"CECILL-2.1",
"CECILL-B",
"CECILL-C",
"ClArtistic",
"CNRI-Jython",
"CNRI-Python-GPL-Compatible",
"CNRI-Python",
"Condor-1.1",
"CPAL-1.0",
"CPL-1.0",
"CPOL-1.02",
"Crossword",
"CrystalStacker",
"CUA-OPL-1.0",
"Cube",
"curl",
"D-FSL-1.0",
"diffmark",
"DOC",
"Dotseqn",
"DSDP",
"dvipdfm",
"ECL-1.0",
"ECL-2.0",
"EFL-1.0",
"EFL-2.0",
"eGenix",
"Entessa",
"EPL-1.0",
"EPL-2.0",
"ErlPL-1.1",
"EUDatagrid",
"EUPL-1.0",
"EUPL-1.1",
"EUPL-1.2",
"Eurosym",
"Fair",
"Frameworx-1.0",
"FreeImage",
"FSFAP",
"FSFUL",
"FSFULLR",
"FTL",
"GFDL-1.1-only",
"GFDL-1.1-or-later",
"GFDL-1.2-only",
"GFDL-1.2-or-later",
"GFDL-1.3-only",
"GFDL-1.3-or-later",
"Giftware",
"GL2PS",
"Glide",
"Glulxe",
"gnuplot",
"GPL-1.0-only",
"GPL-1.0-or-later",
"GPL-2.0-only",
"GPL-2.0-or-later",
"GPL-3.0-only",
"GPL-3.0-or-later",
"gSOAP-1.3b",
"HaskellReport",
"HPND",
"IBM-pibs",
"ICU",
"IJG",
"ImageMagick",
"iMatix",
"Imlib2",
"Info-ZIP",
"Intel-ACPI",
"Intel",
"Interbase-1.0",
"IPA",
"IPL-1.0",
"ISC",
"JasPer-2.0",
"JSON",
"LAL-1.2",
"LAL-1.3",
"Latex2e",
"Leptonica",
"LGPL-2.0-only",
"LGPL-2.0-or-later",
"LGPL-2.1-only",
"LGPL-2.1-or-later",
"LGPL-3.0-only",
"LGPL-3.0-or-later",
"LGPLLR",
"Libpng",
"libtiff",
"LiLiQ-P-1.1",
"LiLiQ-R-1.1",
"LiLiQ-Rplus-1.1",
"LPL-1.0",
"LPL-1.02",
"LPPL-1.0",
"LPPL-1.1",
"LPPL-1.2",
"LPPL-1.3a",
"LPPL-1.3c",
"MakeIndex",
"MirOS",
"MIT-advertising",
"MIT-CMU",
"MIT-enna",
"MIT-feh",
"MIT",
"MITNFA",
"Motosoto",
"mpich2",
"MPL-1.0",
"MPL-1.1",
"MPL-2.0-no-copyleft-exception",
"MPL-2.0",
"MS-PL",
"MS-RL",
"MTLL",
"Multics",
"Mup",
"NASA-1.3",
"Naumen",
"NBPL-1.0",
"NCSA",
"Net-SNMP",
"NetCDF",
"Newsletr",
"NGPL",
"NLOD-1.0",
"NLPL",
"Nokia",
"NOSL",
"Noweb",
"NPL-1.0",
"NPL-1.1",
"NPOSL-3.0",
"NRL",
"NTP",
"OCCT-PL",
"OCLC-2.0",
"ODbL-1.0",
"OFL-1.0",
"OFL-1.1",
"OGTSL",
"OLDAP-1.1",
"OLDAP-1.2",
"OLDAP-1.3",
"OLDAP-1.4",
"OLDAP-2.0.1",
"OLDAP-2.0",
"OLDAP-2.1",
"OLDAP-2.2.1",
"OLDAP-2.2.2",
"OLDAP-2.2",
"OLDAP-2.3",
"OLDAP-2.4",
"OLDAP-2.5",
"OLDAP-2.6",
"OLDAP-2.7",
"OLDAP-2.8",
"OML",
"OpenSSL",
"OPL-1.0",
"OSET-PL-2.1",
"OSL-1.0",
"OSL-1.1",
"OSL-2.0",
"OSL-2.1",
"OSL-3.0",
"PDDL-1.0",
"PHP-3.0",
"PHP-3.01",
"Plexus",
"PostgreSQL",
"psfrag",
"psutils",
"Python-2.0",
"Qhull",
"QPL-1.0",
"Rdisc",
"RHeCos-1.1",
"RPL-1.1",
"RPL-1.5",
"RPSL-1.0",
"RSA-MD",
"RSCPL",
"Ruby",
"SAX-PD",
"Saxpath",
"SCEA",
"Sendmail",
"SGI-B-1.0",
"SGI-B-1.1",
"SGI-B-2.0",
"SimPL-2.0",
"SISSL-1.2",
"SISSL",
"Sleepycat",
"SMLNJ",
"SMPPL",
"SNIA",
"Spencer-86",
"Spencer-94",
"Spencer-99",
"SPL-1.0",
"SugarCRM-1.1.3",
"SWL",
"TCL",
"TCP-wrappers",
"TMate",
"TORQUE-1.1",
"TOSL",
"Unicode-DFS-2015",
"Unicode-DFS-2016",
"Unicode-TOU",
"Unlicense",
"UPL-1.0",
"Vim",
"VOSTROM",
"VSL-1.0",
"W3C-19980720",
"W3C-20150513",
"W3C",
"Watcom-1.0",
"Wsuipa",
"WTFPL",
"X11",
"Xerox",
"XFree86-1.1",
"xinetd",
"Xnet",
"xpp",
"XSkat",
"YPL-1.0",
"YPL-1.1",
"Zed",
"Zend-2.0",
"Zimbra-1.3",
"Zimbra-1.4",
"zlib-acknowledgement",
"Zlib",
"ZPL-1.1",
"ZPL-2.0",
"ZPL-2.1",
# A compiled, public domain list of official SPDX license tags. generated
# using: `python3 -m spdx_license_list print --filter-fsf-or-osi` Only contains
# licenes approved by either FSF to be free/libre software or OSI to be open
# source
APPROVED_LICENSES = [
'0BSD',
'AAL',
'AFL-1.1',
'AFL-1.2',
'AFL-2.0',
'AFL-2.1',
'AFL-3.0',
'AGPL-3.0-only',
'AGPL-3.0-or-later',
'APL-1.0',
'APSL-1.0',
'APSL-1.1',
'APSL-1.2',
'APSL-2.0',
'Apache-1.0',
'Apache-1.1',
'Apache-2.0',
'Artistic-1.0',
'Artistic-1.0-Perl',
'Artistic-1.0-cl8',
'Artistic-2.0',
'BSD-2-Clause',
'BSD-2-Clause-FreeBSD',
'BSD-2-Clause-Patent',
'BSD-3-Clause',
'BSD-3-Clause-Clear',
'BSD-3-Clause-LBNL',
'BSD-4-Clause',
'BSL-1.0',
'BitTorrent-1.1',
'CATOSL-1.1',
'CC-BY-4.0',
'CC-BY-SA-4.0',
'CC0-1.0',
'CDDL-1.0',
'CECILL-2.0',
'CECILL-2.1',
'CECILL-B',
'CECILL-C',
'CNRI-Python',
'CPAL-1.0',
'CPL-1.0',
'CUA-OPL-1.0',
'ClArtistic',
'Condor-1.1',
'ECL-1.0',
'ECL-2.0',
'EFL-1.0',
'EFL-2.0',
'EPL-1.0',
'EPL-2.0',
'EUDatagrid',
'EUPL-1.1',
'EUPL-1.2',
'Entessa',
'FSFAP',
'FTL',
'Fair',
'Frameworx-1.0',
'GFDL-1.1-only',
'GFDL-1.1-or-later',
'GFDL-1.2-only',
'GFDL-1.2-or-later',
'GFDL-1.3-only',
'GFDL-1.3-or-later',
'GPL-2.0-only',
'GPL-2.0-or-later',
'GPL-3.0-only',
'GPL-3.0-or-later',
'HPND',
'IJG',
'IPA',
'IPL-1.0',
'ISC',
'Imlib2',
'Intel',
'LGPL-2.0-only',
'LGPL-2.0-or-later',
'LGPL-2.1-only',
'LGPL-2.1-or-later',
'LGPL-3.0-only',
'LGPL-3.0-or-later',
'LPL-1.0',
'LPL-1.02',
'LPPL-1.2',
'LPPL-1.3a',
'LPPL-1.3c',
'LiLiQ-P-1.1',
'LiLiQ-R-1.1',
'LiLiQ-Rplus-1.1',
'MIT',
'MIT-0',
'MPL-1.0',
'MPL-1.1',
'MPL-2.0',
'MPL-2.0-no-copyleft-exception',
'MS-PL',
'MS-RL',
'MirOS',
'Motosoto',
'Multics',
'NASA-1.3',
'NCSA',
'NGPL',
'NOSL',
'NPL-1.0',
'NPL-1.1',
'NPOSL-3.0',
'NTP',
'Naumen',
'Nokia',
'OCLC-2.0',
'ODbL-1.0',
'OFL-1.0',
'OFL-1.1',
'OGTSL',
'OLDAP-2.3',
'OLDAP-2.7',
'OSET-PL-2.1',
'OSL-1.0',
'OSL-1.1',
'OSL-2.0',
'OSL-2.1',
'OSL-3.0',
'OpenSSL',
'PHP-3.0',
'PHP-3.01',
'PostgreSQL',
'Python-2.0',
'QPL-1.0',
'RPL-1.1',
'RPL-1.5',
'RPSL-1.0',
'RSCPL',
'Ruby',
'SGI-B-2.0',
'SISSL',
'SMLNJ',
'SPL-1.0',
'SimPL-2.0',
'Sleepycat',
'UPL-1.0',
'Unlicense',
'VSL-1.0',
'Vim',
'W3C',
'WTFPL',
'Watcom-1.0',
'X11',
'XFree86-1.1',
'Xnet',
'YPL-1.1',
'ZPL-2.0',
'ZPL-2.1',
'Zend-2.0',
'Zimbra-1.3',
'Zlib',
'gnuplot',
'iMatix',
'xinetd',
]
# an F-Droid addition, until we can enforce a better option
APPROVED_LICENSES.append("PublicDomain")
if __name__ == "__main__":
main()

View file

@ -179,6 +179,100 @@ class LintTest(unittest.TestCase):
logging.debug(warn)
self.assertTrue(anywarns)
def test_check_license_tag_no_custom_pass(self):
config = dict()
fdroidserver.common.fill_config_defaults(config)
fdroidserver.common.config = config
fdroidserver.lint.config = config
app = fdroidserver.metadata.App()
app.License = "GPL-3.0-or-later"
anywarns = False
for warn in fdroidserver.lint.check_license_tag(app):
anywarns = True
logging.debug(warn)
self.assertFalse(anywarns)
def test_check_license_tag_no_custom_fail(self):
config = dict()
fdroidserver.common.fill_config_defaults(config)
fdroidserver.common.config = config
fdroidserver.lint.config = config
app = fdroidserver.metadata.App()
app.License = "Adobe-2006"
anywarns = False
for warn in fdroidserver.lint.check_license_tag(app):
anywarns = True
logging.debug(warn)
self.assertTrue(anywarns)
def test_check_license_tag_with_custom_pass(self):
config = dict()
fdroidserver.common.fill_config_defaults(config)
fdroidserver.common.config = config
fdroidserver.lint.config = config
config['lint_licenses'] = ['fancy-license', 'GPL-3.0-or-later']
app = fdroidserver.metadata.App()
app.License = "fancy-license"
anywarns = False
for warn in fdroidserver.lint.check_license_tag(app):
anywarns = True
logging.debug(warn)
self.assertFalse(anywarns)
def test_check_license_tag_with_custom_fail(self):
config = dict()
fdroidserver.common.fill_config_defaults(config)
fdroidserver.common.config = config
fdroidserver.lint.config = config
config['lint_licenses'] = ['fancy-license', 'GPL-3.0-or-later']
app = fdroidserver.metadata.App()
app.License = "Apache-2.0"
anywarns = False
for warn in fdroidserver.lint.check_license_tag(app):
anywarns = True
logging.debug(warn)
self.assertTrue(anywarns)
def test_check_license_tag_with_custom_empty(self):
config = dict()
fdroidserver.common.fill_config_defaults(config)
fdroidserver.common.config = config
fdroidserver.lint.config = config
config['lint_licenses'] = []
app = fdroidserver.metadata.App()
app.License = "Apache-2.0"
anywarns = False
for warn in fdroidserver.lint.check_license_tag(app):
anywarns = True
logging.debug(warn)
self.assertTrue(anywarns)
def test_check_license_tag_disabled(self):
config = dict()
fdroidserver.common.fill_config_defaults(config)
fdroidserver.common.config = config
fdroidserver.lint.config = config
config['lint_licenses'] = None
app = fdroidserver.metadata.App()
app.License = "Apache-2.0"
anywarns = False
for warn in fdroidserver.lint.check_license_tag(app):
anywarns = True
logging.debug(warn)
self.assertFalse(anywarns)
if __name__ == "__main__":
os.chdir(os.path.dirname(__file__))