Do basic sanity check of a package's most important fields when checking for problems

This commit is contained in:
Martchus 2023-06-03 18:49:02 +02:00
parent 77000ac7d3
commit 524b16815a
3 changed files with 83 additions and 0 deletions

View File

@ -443,6 +443,84 @@ bool Package::isArchAny() const
return true;
}
static bool containsUnexpectedCharacters(std::string_view value)
{
for (auto c : value) {
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
continue;
}
switch (c) {
case '+':
case '-':
case '_':
case '.':
continue;
default:
return true;
}
}
return false;
}
static bool containsUnprintableCharacters(std::string_view value)
{
for (auto c : value) {
if (c < ' ' || c >= 127) {
return true;
}
}
return false;
}
#define CHECK_FIELD_EMPTY(field) \
if (field.empty()) { \
problems.emplace_back(#field " is empty"); \
}
#define CHECK_FIELD_FOR_UNEXPECTED_CHARS(field) \
if (containsUnexpectedCharacters(field)) { \
problems.emplace_back(#field " contains unexpected characters"); \
}
#define CHECK_FIELD_FOR_UNPRINTABLE_CHARS(field) \
if (containsUnprintableCharacters(field)) { \
problems.emplace_back(#field " contains unprintable or non-ASCII characters"); \
}
#define CHECK_FIELD_STRICT(field) \
CHECK_FIELD_EMPTY(field) \
CHECK_FIELD_FOR_UNEXPECTED_CHARS(field)
#define CHECK_FIELD_RELAXED(field) \
CHECK_FIELD_EMPTY(field) \
CHECK_FIELD_FOR_UNPRINTABLE_CHARS(field)
/*!
* \brief Performs a basic sanity check of the package's fields.
* \returns Returns an empty vector if no problems were found; otherwise returns the problem descriptions.
*/
std::vector<std::string> Package::validate() const
{
// check basic fields
auto problems = std::vector<std::string>();
CHECK_FIELD_STRICT(name)
CHECK_FIELD_RELAXED(version)
CHECK_FIELD_STRICT(arch)
// check dependencies
const auto checkDeps = sourceInfo ? sourceInfo->checkDependencies : std::vector<Dependency>();
const auto makeDeps = sourceInfo ? sourceInfo->makeDependencies : std::vector<Dependency>();
for (const auto &deps : { dependencies, optionalDependencies, provides, conflicts, replaces, checkDeps, makeDeps }) {
for (const auto &dep : deps) {
if (dep.name.empty()) {
problems.emplace_back("dependency is empty");
return problems;
}
if (containsUnexpectedCharacters(dep.name)) {
problems.emplace_back("dependency contains unexpected characters");
return problems;
}
}
}
return problems;
}
DependencySetBase::iterator DependencySet::find(const Dependency &dependency)
{
for (auto range = equal_range(dependency.name); range.first != range.second; ++range.first) {

View File

@ -417,6 +417,7 @@ struct LIBPKG_EXPORT Package : public PackageBase,
std::vector<std::string> processDllsReferencedByImportLibs(std::set<std::string> &&dllsReferencedByImportLibs);
bool addDepsAndProvidesFromOtherPackage(const Package &otherPackage, bool force = false);
bool isArchAny() const;
std::vector<std::string> validate() const;
using ReflectiveRapidJSON::JsonSerializable<Package>::fromJson;
using ReflectiveRapidJSON::JsonSerializable<Package>::toJson;
using ReflectiveRapidJSON::JsonSerializable<Package>::toJsonDocument;

View File

@ -466,6 +466,10 @@ void CheckForProblems::run()
RepositoryProblem{ .desc = "configured local package directory \"" % db->localPkgDir + "\" is not a directory" });
}
db->allPackages([&](LibPkg::StorageID, std::shared_ptr<LibPkg::Package> &&package) {
const auto packageProblems = package->validate();
for (const auto &problem : packageProblems) {
problems.emplace_back(RepositoryProblem{ .desc = problem, .pkg = package->name });
}
if (!package->packageInfo) {
problems.emplace_back(RepositoryProblem{ .desc = "no package info present", .pkg = package->name });
return false;