Merge pull request #11888 from ethereum/spdx-license-handling-10145

Properly detect multiple licenses and validate them.
This commit is contained in:
chriseth 2021-09-16 14:18:17 +02:00 committed by GitHub
commit 4284499180
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 68 additions and 46 deletions

View File

@ -22,6 +22,7 @@ Bugfixes:
* Commandline Interface: Disallow the ``--experimental-via-ir`` option in Standard JSON, Assembler and Linker modes. * Commandline Interface: Disallow the ``--experimental-via-ir`` option in Standard JSON, Assembler and Linker modes.
* Opcode Optimizer: Prevent the optimizer from running multiple times to avoid potential bytecode differences for referenced code. * Opcode Optimizer: Prevent the optimizer from running multiple times to avoid potential bytecode differences for referenced code.
* Name Resolver: Fix that when importing an aliased symbol using ``import {AliasedName} from "a.sol"`` it would use the original name of the symbol and not the aliased one. * Name Resolver: Fix that when importing an aliased symbol using ``import {AliasedName} from "a.sol"`` it would use the original name of the symbol and not the aliased one.
* Parser: Properly check for multiple SPDX license identifiers next to each other and validate them.
* SMTChecker: Fix false negative caused by ``push`` on storage array references returned by internal functions. * SMTChecker: Fix false negative caused by ``push`` on storage array references returned by internal functions.
* SMTChecker: Fix false positive in external calls from constructors. * SMTChecker: Fix false positive in external calls from constructors.
* SMTChecker: Fix internal error on some multi-source uses of ``abi.*``, cryptographic functions and constants. * SMTChecker: Fix internal error on some multi-source uses of ``abi.*``, cryptographic functions and constants.

View File

@ -34,6 +34,7 @@
#include <libyul/backends/evm/EVMDialect.h> #include <libyul/backends/evm/EVMDialect.h>
#include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <cctype> #include <cctype>
#include <vector> #include <vector>
#include <regex> #include <regex>
@ -2078,7 +2079,8 @@ bool Parser::variableDeclarationStart()
optional<string> Parser::findLicenseString(std::vector<ASTPointer<ASTNode>> const& _nodes) optional<string> Parser::findLicenseString(std::vector<ASTPointer<ASTNode>> const& _nodes)
{ {
// We circumvent the scanner here, because it skips non-docstring comments. // We circumvent the scanner here, because it skips non-docstring comments.
static regex const licenseRegex("SPDX-License-Identifier:\\s*([a-zA-Z0-9 ()+.-]+)"); static regex const licenseNameRegex("([a-zA-Z0-9 ()+.-]+)");
static regex const licenseDeclarationRegex("SPDX-License-Identifier:\\s*(.+?)([\n\r]|(\\*/))");
// Search inside all parts of the source not covered by parsed nodes. // Search inside all parts of the source not covered by parsed nodes.
// This will leave e.g. "global comments". // This will leave e.g. "global comments".
@ -2093,21 +2095,33 @@ optional<string> Parser::findLicenseString(std::vector<ASTPointer<ASTNode>> cons
sequencesToSearch.emplace_back(source.begin() + node->location().end, source.end()); sequencesToSearch.emplace_back(source.begin() + node->location().end, source.end());
} }
vector<string> matches; vector<string> licenseNames;
for (auto const& [start, end]: sequencesToSearch) for (auto const& [start, end]: sequencesToSearch)
{ {
smatch match; auto declarationsBegin = std::sregex_iterator(start, end, licenseDeclarationRegex);
if (regex_search(start, end, match, licenseRegex)) auto declarationsEnd = std::sregex_iterator();
{
string license{boost::trim_copy(string(match[1]))}; for (std::sregex_iterator declIt = declarationsBegin; declIt != declarationsEnd; ++declIt)
if (!license.empty()) if (!declIt->empty())
matches.emplace_back(std::move(license)); {
} string license = boost::trim_copy(string((*declIt)[1]));
licenseNames.emplace_back(std::move(license));
}
} }
if (matches.size() == 1) if (licenseNames.size() == 1)
return matches.front(); {
else if (matches.empty()) string const& license = licenseNames.front();
if (regex_match(license, licenseNameRegex))
return license;
else
parserError(
1114_error,
{-1, -1, m_scanner->currentLocation().sourceName},
"Invalid SPDX license identifier."
);
}
else if (licenseNames.empty())
parserWarning( parserWarning(
1878_error, 1878_error,
{-1, -1, m_scanner->currentLocation().sourceName}, {-1, -1, m_scanner->currentLocation().sourceName},

View File

@ -120,7 +120,11 @@ done < <(
# a variable declaration. # a variable declaration.
grep -v -E 'revertStatement/non_called.sol' | grep -v -E 'revertStatement/non_called.sol' |
# Skipping a test with "let basefee := ..." # Skipping a test with "let basefee := ..."
grep -v -E 'inlineAssembly/basefee_berlin_function.sol' grep -v -E 'inlineAssembly/basefee_berlin_function.sol' |
# Skipping license error, unrelated to the grammar
grep -v -E 'license/license_double5.sol' |
grep -v -E 'license/license_hidden_unicode.sol' |
grep -v -E 'license/license_unicode.sol'
) )
YUL_FILES=() YUL_FILES=()

View File

@ -457,28 +457,6 @@ BOOST_AUTO_TEST_CASE(metadata_license_gpl3_or_apache2)
BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0 OR Apache-2.0"); BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0 OR Apache-2.0");
} }
BOOST_AUTO_TEST_CASE(metadata_license_ignored_unicode)
{
char const* sourceCode = R"(
// SPDX-License-Identifier: ⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒
pragma solidity >=0.0;
contract C {
}
)";
BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == nullopt);
}
BOOST_AUTO_TEST_CASE(metadata_license_ignored_stray_unicode)
{
char const* sourceCode = R"(
// SPDX-License-Identifier: GPL-3.0 ⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒
pragma solidity >=0.0;
contract C {
}
)";
BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
}
BOOST_AUTO_TEST_CASE(metadata_license_bidi_marks) BOOST_AUTO_TEST_CASE(metadata_license_bidi_marks)
{ {
char const* sourceCode = char const* sourceCode =
@ -570,15 +548,6 @@ BOOST_AUTO_TEST_CASE(metadata_license_natspec_multiline)
BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
} }
BOOST_AUTO_TEST_CASE(metadata_license_no_whitespace)
{
char const* sourceCode = R"(
//SPDX-License-Identifier:GPL-3.0
contract C {}
)";
BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
}
BOOST_AUTO_TEST_CASE(metadata_license_no_whitespace_multiline) BOOST_AUTO_TEST_CASE(metadata_license_no_whitespace_multiline)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(
@ -597,6 +566,15 @@ BOOST_AUTO_TEST_CASE(metadata_license_nonempty_line)
BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
} }
BOOST_AUTO_TEST_CASE(metadata_license_no_whitespace)
{
char const* sourceCode = R"(
//SPDX-License-Identifier:GPL-3.0
contract C {}
)";
BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

View File

@ -0,0 +1,3 @@
// SPDX-License-Identifier: GPL-3.0 AND GPL-2.0
contract C {}
// ----

View File

@ -0,0 +1,3 @@
// SPDX-License-Identifier: GPL-3.0 OR GPL-2.0
contract C {}
// ----

View File

@ -0,0 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
// SPDX-License-Identifier: MIT
contract C {}
// ----
// ParserError 3716: Multiple SPDX license identifiers found in source file. Use "AND" or "OR" to combine multiple licenses. Please see https://spdx.org for more information.

View File

@ -0,0 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
// SPDX-License-Identifier: GPL-3.0
contract C {}
// ----
// ParserError 3716: Multiple SPDX license identifiers found in source file. Use "AND" or "OR" to combine multiple licenses. Please see https://spdx.org for more information.

View File

@ -0,0 +1,5 @@
/* SPDX-License-Identifier: GPL-3.0 OR GPL-2.0
SPDX-License-Identifier: GPL-3.0 OR GPL-2.0 */
contract C {}
// ----
// ParserError 3716: Multiple SPDX license identifiers found in source file. Use "AND" or "OR" to combine multiple licenses. Please see https://spdx.org for more information.

View File

@ -0,0 +1,4 @@
/* SPDX-License-Identifier: GPL-3.0 OR GPL-2.0 */ /* SPDX-License-Identifier: GPL-3.0 OR GPL-2.0 */
contract C {}
// ----
// ParserError 3716: Multiple SPDX license identifiers found in source file. Use "AND" or "OR" to combine multiple licenses. Please see https://spdx.org for more information.

View File

@ -1,4 +1,4 @@
// This is parsed as GPL-3.0:
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
contract C {} contract C {}
// ---- // ----
// ParserError 1114: Invalid SPDX license identifier.

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: // SPDX-License-Identifier:
contract C {} contract C {}
// ---- // ----
// Warning 1878: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. // ParserError 1114: Invalid SPDX license identifier.