From f6644c07cbfcbc6f82d6656905f58dca35fa4fec Mon Sep 17 00:00:00 2001 From: Kunal Mehta Date: Tue, 27 Sep 2016 19:56:07 -0700 Subject: [PATCH] registration: Improve license-name validation Our hardcoded enum list in the extension.json schema for license-name values was incomplete and did not cover the full SPDX license identifier specification, which includes things like "AND" for specifying multiple licenses. Composer already has solid code in a library to do this validation, so let's use it! This updates both the validateRegistrationFile.php and ExtensionJsonValidationTest.php to use the composer/spdx-licenses library (a new development dependency) to ensure the license-name field is a valid SPDX identifier. Also fix a silly typo in the validateRegistrationFile script which prevented it from running, and use ::class so it will be easier to detect typos like that in the future. Bug: T146862 Change-Id: Ibb8973ed7950ae81c90558f9630f73746b2aff2c --- composer.json | 1 + docs/extension.schema.json | 219 +----------------- docs/extension.schema.v1.json | 219 +----------------- maintenance/validateRegistrationFile.php | 28 ++- .../structure/ExtensionJsonValidationTest.php | 25 +- 5 files changed, 50 insertions(+), 442 deletions(-) diff --git a/composer.json b/composer.json index eedaa4e55c..9424a73bff 100644 --- a/composer.json +++ b/composer.json @@ -44,6 +44,7 @@ "zordius/lightncandy": "0.23" }, "require-dev": { + "composer/spdx-licenses": "1.1.4", "jakub-onderka/php-parallel-lint": "0.9.2", "justinrainbow/json-schema": "~3.0", "mediawiki/mediawiki-codesniffer": "0.7.2", diff --git a/docs/extension.schema.json b/docs/extension.schema.json index 384bfb4b10..84a404a2e2 100644 --- a/docs/extension.schema.json +++ b/docs/extension.schema.json @@ -51,224 +51,7 @@ }, "license-name": { "type": "string", - "description": "Short identifier for the license under which the extension is released.", - "enum": [ - "AFL-1.1", - "AFL-1.2", - "AFL-2.0", - "AFL-2.1", - "AFL-3.0", - "APL-1.0", - "Aladdin", - "ANTLR-PD", - "Apache-1.0", - "Apache-1.1", - "Apache-2.0", - "APSL-1.0", - "APSL-1.1", - "APSL-1.2", - "APSL-2.0", - "Artistic-1.0", - "Artistic-1.0-cl8", - "Artistic-1.0-Perl", - "Artistic-2.0", - "AAL", - "BitTorrent-1.0", - "BitTorrent-1.1", - "BSL-1.0", - "BSD-2-Clause", - "BSD-2-Clause-FreeBSD", - "BSD-2-Clause-NetBSD", - "BSD-3-Clause", - "BSD-3-Clause-Clear", - "BSD-4-Clause", - "BSD-4-Clause-UC", - "CECILL-1.0", - "CECILL-1.1", - "CECILL-2.0", - "CECILL-B", - "CECILL-C", - "ClArtistic", - "CNRI-Python", - "CNRI-Python-GPL-Compatible", - "CPOL-1.02", - "CDDL-1.0", - "CDDL-1.1", - "CPAL-1.0", - "CPL-1.0", - "CATOSL-1.1", - "Condor-1.1", - "CC-BY-1.0", - "CC-BY-2.0", - "CC-BY-2.5", - "CC-BY-3.0", - "CC-BY-ND-1.0", - "CC-BY-ND-2.0", - "CC-BY-ND-2.5", - "CC-BY-ND-3.0", - "CC-BY-NC-1.0", - "CC-BY-NC-2.0", - "CC-BY-NC-2.5", - "CC-BY-NC-3.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-SA-1.0", - "CC-BY-NC-SA-2.0", - "CC-BY-NC-SA-2.5", - "CC-BY-NC-SA-3.0", - "CC-BY-SA-1.0", - "CC-BY-SA-2.0", - "CC-BY-SA-2.5", - "CC-BY-SA-3.0", - "CC0-1.0", - "CUA-OPL-1.0", - "D-FSL-1.0", - "WTFPL", - "EPL-1.0", - "eCos-2.0", - "ECL-1.0", - "ECL-2.0", - "EFL-1.0", - "EFL-2.0", - "Entessa", - "ErlPL-1.1", - "EUDatagrid", - "EUPL-1.0", - "EUPL-1.1", - "Fair", - "Frameworx-1.0", - "FTL", - "AGPL-1.0", - "AGPL-3.0", - "GFDL-1.1", - "GFDL-1.2", - "GFDL-1.3", - "GPL-1.0", - "GPL-1.0+", - "GPL-2.0", - "GPL-2.0+", - "GPL-2.0-with-autoconf-exception", - "GPL-2.0-with-bison-exception", - "GPL-2.0-with-classpath-exception", - "GPL-2.0-with-font-exception", - "GPL-2.0-with-GCC-exception", - "GPL-3.0", - "GPL-3.0+", - "GPL-3.0-with-autoconf-exception", - "GPL-3.0-with-GCC-exception", - "LGPL-2.1", - "LGPL-2.1+", - "LGPL-3.0", - "LGPL-3.0+", - "LGPL-2.0", - "LGPL-2.0+", - "gSOAP-1.3b", - "HPND", - "IBM-pibs", - "IPL-1.0", - "Imlib2", - "IJG", - "Intel", - "IPA", - "ISC", - "JSON", - "LPPL-1.3a", - "LPPL-1.0", - "LPPL-1.1", - "LPPL-1.2", - "LPPL-1.3c", - "Libpng", - "LPL-1.02", - "LPL-1.0", - "MS-PL", - "MS-RL", - "MirOS", - "MIT", - "Motosoto", - "MPL-1.0", - "MPL-1.1", - "MPL-2.0", - "MPL-2.0-no-copyleft-exception", - "Multics", - "NASA-1.3", - "Naumen", - "NBPL-1.0", - "NGPL", - "NOSL", - "NPL-1.0", - "NPL-1.1", - "Nokia", - "NPOSL-3.0", - "NTP", - "OCLC-2.0", - "ODbL-1.0", - "PDDL-1.0", - "OGTSL", - "OLDAP-2.2.2", - "OLDAP-1.1", - "OLDAP-1.2", - "OLDAP-1.3", - "OLDAP-1.4", - "OLDAP-2.0", - "OLDAP-2.0.1", - "OLDAP-2.1", - "OLDAP-2.2", - "OLDAP-2.2.1", - "OLDAP-2.3", - "OLDAP-2.4", - "OLDAP-2.5", - "OLDAP-2.6", - "OLDAP-2.7", - "OPL-1.0", - "OSL-1.0", - "OSL-2.0", - "OSL-2.1", - "OSL-3.0", - "OLDAP-2.8", - "OpenSSL", - "PHP-3.0", - "PHP-3.01", - "PostgreSQL", - "Python-2.0", - "QPL-1.0", - "RPSL-1.0", - "RPL-1.1", - "RPL-1.5", - "RHeCos-1.1", - "RSCPL", - "Ruby", - "SAX-PD", - "SGI-B-1.0", - "SGI-B-1.1", - "SGI-B-2.0", - "OFL-1.0", - "OFL-1.1", - "SimPL-2.0", - "Sleepycat", - "SMLNJ", - "SugarCRM-1.1.3", - "SISSL", - "SISSL-1.2", - "SPL-1.0", - "Watcom-1.0", - "NCSA", - "VSL-1.0", - "W3C", - "WXwindows", - "Xnet", - "X11", - "XFree86-1.1", - "YPL-1.0", - "YPL-1.1", - "Zimbra-1.3", - "Zlib", - "ZPL-1.1", - "ZPL-2.0", - "ZPL-2.1", - "Unlicense" - ] + "description": "SPDX identifier for the license under which the extension is released." }, "requires": { "type": "object", diff --git a/docs/extension.schema.v1.json b/docs/extension.schema.v1.json index c4a1a8dbd0..94999270a1 100644 --- a/docs/extension.schema.v1.json +++ b/docs/extension.schema.v1.json @@ -51,224 +51,7 @@ }, "license-name": { "type": "string", - "description": "Short identifier for the license under which the extension is released.", - "enum": [ - "AFL-1.1", - "AFL-1.2", - "AFL-2.0", - "AFL-2.1", - "AFL-3.0", - "APL-1.0", - "Aladdin", - "ANTLR-PD", - "Apache-1.0", - "Apache-1.1", - "Apache-2.0", - "APSL-1.0", - "APSL-1.1", - "APSL-1.2", - "APSL-2.0", - "Artistic-1.0", - "Artistic-1.0-cl8", - "Artistic-1.0-Perl", - "Artistic-2.0", - "AAL", - "BitTorrent-1.0", - "BitTorrent-1.1", - "BSL-1.0", - "BSD-2-Clause", - "BSD-2-Clause-FreeBSD", - "BSD-2-Clause-NetBSD", - "BSD-3-Clause", - "BSD-3-Clause-Clear", - "BSD-4-Clause", - "BSD-4-Clause-UC", - "CECILL-1.0", - "CECILL-1.1", - "CECILL-2.0", - "CECILL-B", - "CECILL-C", - "ClArtistic", - "CNRI-Python", - "CNRI-Python-GPL-Compatible", - "CPOL-1.02", - "CDDL-1.0", - "CDDL-1.1", - "CPAL-1.0", - "CPL-1.0", - "CATOSL-1.1", - "Condor-1.1", - "CC-BY-1.0", - "CC-BY-2.0", - "CC-BY-2.5", - "CC-BY-3.0", - "CC-BY-ND-1.0", - "CC-BY-ND-2.0", - "CC-BY-ND-2.5", - "CC-BY-ND-3.0", - "CC-BY-NC-1.0", - "CC-BY-NC-2.0", - "CC-BY-NC-2.5", - "CC-BY-NC-3.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-SA-1.0", - "CC-BY-NC-SA-2.0", - "CC-BY-NC-SA-2.5", - "CC-BY-NC-SA-3.0", - "CC-BY-SA-1.0", - "CC-BY-SA-2.0", - "CC-BY-SA-2.5", - "CC-BY-SA-3.0", - "CC0-1.0", - "CUA-OPL-1.0", - "D-FSL-1.0", - "WTFPL", - "EPL-1.0", - "eCos-2.0", - "ECL-1.0", - "ECL-2.0", - "EFL-1.0", - "EFL-2.0", - "Entessa", - "ErlPL-1.1", - "EUDatagrid", - "EUPL-1.0", - "EUPL-1.1", - "Fair", - "Frameworx-1.0", - "FTL", - "AGPL-1.0", - "AGPL-3.0", - "GFDL-1.1", - "GFDL-1.2", - "GFDL-1.3", - "GPL-1.0", - "GPL-1.0+", - "GPL-2.0", - "GPL-2.0+", - "GPL-2.0-with-autoconf-exception", - "GPL-2.0-with-bison-exception", - "GPL-2.0-with-classpath-exception", - "GPL-2.0-with-font-exception", - "GPL-2.0-with-GCC-exception", - "GPL-3.0", - "GPL-3.0+", - "GPL-3.0-with-autoconf-exception", - "GPL-3.0-with-GCC-exception", - "LGPL-2.1", - "LGPL-2.1+", - "LGPL-3.0", - "LGPL-3.0+", - "LGPL-2.0", - "LGPL-2.0+", - "gSOAP-1.3b", - "HPND", - "IBM-pibs", - "IPL-1.0", - "Imlib2", - "IJG", - "Intel", - "IPA", - "ISC", - "JSON", - "LPPL-1.3a", - "LPPL-1.0", - "LPPL-1.1", - "LPPL-1.2", - "LPPL-1.3c", - "Libpng", - "LPL-1.02", - "LPL-1.0", - "MS-PL", - "MS-RL", - "MirOS", - "MIT", - "Motosoto", - "MPL-1.0", - "MPL-1.1", - "MPL-2.0", - "MPL-2.0-no-copyleft-exception", - "Multics", - "NASA-1.3", - "Naumen", - "NBPL-1.0", - "NGPL", - "NOSL", - "NPL-1.0", - "NPL-1.1", - "Nokia", - "NPOSL-3.0", - "NTP", - "OCLC-2.0", - "ODbL-1.0", - "PDDL-1.0", - "OGTSL", - "OLDAP-2.2.2", - "OLDAP-1.1", - "OLDAP-1.2", - "OLDAP-1.3", - "OLDAP-1.4", - "OLDAP-2.0", - "OLDAP-2.0.1", - "OLDAP-2.1", - "OLDAP-2.2", - "OLDAP-2.2.1", - "OLDAP-2.3", - "OLDAP-2.4", - "OLDAP-2.5", - "OLDAP-2.6", - "OLDAP-2.7", - "OPL-1.0", - "OSL-1.0", - "OSL-2.0", - "OSL-2.1", - "OSL-3.0", - "OLDAP-2.8", - "OpenSSL", - "PHP-3.0", - "PHP-3.01", - "PostgreSQL", - "Python-2.0", - "QPL-1.0", - "RPSL-1.0", - "RPL-1.1", - "RPL-1.5", - "RHeCos-1.1", - "RSCPL", - "Ruby", - "SAX-PD", - "SGI-B-1.0", - "SGI-B-1.1", - "SGI-B-2.0", - "OFL-1.0", - "OFL-1.1", - "SimPL-2.0", - "Sleepycat", - "SMLNJ", - "SugarCRM-1.1.3", - "SISSL", - "SISSL-1.2", - "SPL-1.0", - "Watcom-1.0", - "NCSA", - "VSL-1.0", - "W3C", - "WXwindows", - "Xnet", - "X11", - "XFree86-1.1", - "YPL-1.0", - "YPL-1.1", - "Zimbra-1.3", - "Zlib", - "ZPL-1.1", - "ZPL-2.0", - "ZPL-2.1", - "Unlicense" - ] + "description": "SPDX identifier for the license under which the extension is released." }, "requires": { "type": "object", diff --git a/maintenance/validateRegistrationFile.php b/maintenance/validateRegistrationFile.php index bd34a50eab..7dd0907f64 100644 --- a/maintenance/validateRegistrationFile.php +++ b/maintenance/validateRegistrationFile.php @@ -2,14 +2,21 @@ require_once __DIR__ . '/Maintenance.php'; +use Composer\Spdx\SpdxLicenses; +use JsonSchema\Validator; + class ValidateRegistrationFile extends Maintenance { public function __construct() { parent::__construct(); $this->addArg( 'path', 'Path to extension.json/skin.json file.', true ); } public function execute() { - if ( !class_exists( 'JsonSchema\Validato' ) ) { + if ( !class_exists( Validator::class ) ) { $this->error( 'The JsonSchema library cannot be found, please install it through composer.', 1 ); + } elseif ( !class_exists( SpdxLicenses::class ) ) { + $this->error( + 'The spdx-licenses library cannot be found, please install it through composer.', 1 + ); } $path = $this->getArg( 0 ); @@ -38,14 +45,29 @@ class ValidateRegistrationFile extends Maintenance { $this->output( "Warning: $path is using a deprecated schema, and should be updated to " . ExtensionRegistry::MANIFEST_VERSION . "\n" ); } - $validator = new JsonSchema\Validator; + + $licenseError = false; + // Check if it's a string, if not, schema validation will display an error + if ( isset( $data->{'license-name'} ) && is_string( $data->{'license-name'} ) ) { + $licenses = new SpdxLicenses(); + $valid = $licenses->validate( $data->{'license-name'} ); + if ( !$valid ) { + $licenseError = '[license-name] Invalid SPDX license identifier, ' + . 'see '; + } + } + + $validator = new Validator; $validator->check( $data, (object) [ '$ref' => 'file://' . $schemaPath ] ); - if ( $validator->isValid() ) { + if ( $validator->isValid() && !$licenseError ) { $this->output( "$path validates against the version $version schema!\n" ); } else { foreach ( $validator->getErrors() as $error ) { $this->output( "[{$error['property']}] {$error['message']}\n" ); } + if ( $licenseError ) { + $this->output( "$licenseError\n" ); + } $this->error( "$path does not validate.", 1 ); } } diff --git a/tests/phpunit/structure/ExtensionJsonValidationTest.php b/tests/phpunit/structure/ExtensionJsonValidationTest.php index 711eab647e..ad61284120 100644 --- a/tests/phpunit/structure/ExtensionJsonValidationTest.php +++ b/tests/phpunit/structure/ExtensionJsonValidationTest.php @@ -16,6 +16,9 @@ * http://www.gnu.org/copyleft/gpl.html */ +use Composer\Spdx\SpdxLicenses; +use JsonSchema\Validator; + /** * Validates all loaded extensions and skins using the ExtensionRegistry * against the extension.json schema in the docs/ folder. @@ -24,7 +27,7 @@ class ExtensionJsonValidationTest extends PHPUnit_Framework_TestCase { public function setUp() { parent::setUp(); - if ( !class_exists( 'JsonSchema\Uri\UriRetriever' ) ) { + if ( !class_exists( Validator::class ) ) { $this->markTestSkipped( 'The JsonSchema library cannot be found,' . ' please install it through composer to run extension.json validation tests.' @@ -75,9 +78,22 @@ class ExtensionJsonValidationTest extends PHPUnit_Framework_TestCase { "$path is using a non-supported schema version" ); - $validator = new JsonSchema\Validator; + $licenseError = false; + if ( class_exists( SpdxLicenses::class ) && isset( $data->{'license-name'} ) + // Check if it's a string, if not, schema validation will display an error + && is_string( $data->{'license-name'} ) + ) { + $licenses = new SpdxLicenses(); + $valid = $licenses->validate( $data->{'license-name'} ); + if ( !$valid ) { + $licenseError = '[license-name] Invalid SPDX license identifier, ' + . 'see '; + } + } + + $validator = new Validator; $validator->check( $data, (object) [ '$ref' => 'file://' . $schemaPath ] ); - if ( $validator->isValid() ) { + if ( $validator->isValid() && !$licenseError ) { // All good. $this->assertTrue( true ); } else { @@ -85,6 +101,9 @@ class ExtensionJsonValidationTest extends PHPUnit_Framework_TestCase { foreach ( $validator->getErrors() as $error ) { $out .= "[{$error['property']}] {$error['message']}\n"; } + if ( $licenseError ) { + $out .= "$licenseError\n"; + } $this->assertTrue( false, $out ); } } -- 2.20.1