Properly detect multiple licenses and validate them.

This commit is contained in:
Marenz 2021-09-02 15:25:10 +02:00
parent 0fa24c786b
commit c81814915c
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.
* 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.
* 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 positive in external calls from constructors.
* 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 <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <cctype>
#include <vector>
#include <regex>
@ -2078,7 +2079,8 @@ bool Parser::variableDeclarationStart()
optional<string> Parser::findLicenseString(std::vector<ASTPointer<ASTNode>> const& _nodes)
{
// 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.
// 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());
}
vector<string> matches;
vector<string> licenseNames;
for (auto const& [start, end]: sequencesToSearch)
{
smatch match;
if (regex_search(start, end, match, licenseRegex))
{
string license{boost::trim_copy(string(match[1]))};
if (!license.empty())
matches.emplace_back(std::move(license));
}
auto declarationsBegin = std::sregex_iterator(start, end, licenseDeclarationRegex);
auto declarationsEnd = std::sregex_iterator();
for (std::sregex_iterator declIt = declarationsBegin; declIt != declarationsEnd; ++declIt)
if (!declIt->empty())
{
string license = boost::trim_copy(string((*declIt)[1]));
licenseNames.emplace_back(std::move(license));
}
}
if (matches.size() == 1)
return matches.front();
else if (matches.empty())
if (licenseNames.size() == 1)
{
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(
1878_error,
{-1, -1, m_scanner->currentLocation().sourceName},

View File

@ -120,7 +120,11 @@ done < <(
# a variable declaration.
grep -v -E 'revertStatement/non_called.sol' |
# 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=()

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_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)
{
char const* sourceCode =
@ -570,15 +548,6 @@ BOOST_AUTO_TEST_CASE(metadata_license_natspec_multiline)
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)
{
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_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()
}

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
contract C {}
// ----
// ParserError 1114: Invalid SPDX license identifier.

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier:
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.