Skip to content

Commit 5f7a697

Browse files
authored
Fix various potential crashes in the indexer resolver (#774)
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent 7a1e3b9 commit 5f7a697

7 files changed

Lines changed: 69 additions & 13 deletions

File tree

src/index/index.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,11 +531,18 @@ auto main(int argc, char *argv[]) noexcept -> int {
531531
} catch (const sourcemeta::core::SchemaUnknownBaseDialectError &error) {
532532
std::cerr << "error: " << error.what() << "\n";
533533
return EXIT_FAILURE;
534+
} catch (const sourcemeta::core::SchemaUnknownDialectError &error) {
535+
std::cerr << "error: " << error.what() << "\n";
536+
return EXIT_FAILURE;
534537
} catch (const sourcemeta::one::BuildTooManyDirectoryEntriesError &error) {
535538
std::cerr << "error: " << error.what() << "\n at path "
536539
<< error.path().string() << "\n with count " << error.count()
537540
<< "\n";
538541
return EXIT_FAILURE;
542+
} catch (const sourcemeta::one::ResolverNotASchemaError &error) {
543+
std::cerr << "error: " << error.what() << "\n at " << error.path().string()
544+
<< "\n";
545+
return EXIT_FAILURE;
539546
} catch (const sourcemeta::one::ResolverOutsideBaseError &error) {
540547
std::cerr << "error: " << error.what() << "\n at " << error.uri()
541548
<< "\n with base " << error.base() << "\n";

src/resolver/include/sourcemeta/one/resolver_error.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,29 @@
33

44
#include <sourcemeta/core/json.h>
55

6-
#include <exception> // std::exception
7-
#include <utility> // std::move
6+
#include <exception> // std::exception
7+
#include <filesystem> // std::filesystem::path
8+
#include <utility> // std::move
89

910
namespace sourcemeta::one {
1011

12+
class ResolverNotASchemaError : public std::exception {
13+
public:
14+
ResolverNotASchemaError(std::filesystem::path path)
15+
: path_{std::move(path)} {}
16+
17+
[[nodiscard]] auto what() const noexcept -> const char * override {
18+
return "The file does not contain a valid JSON Schema";
19+
}
20+
21+
[[nodiscard]] auto path() const noexcept -> const std::filesystem::path & {
22+
return this->path_;
23+
}
24+
25+
private:
26+
std::filesystem::path path_;
27+
};
28+
1129
class ResolverOutsideBaseError : public std::exception {
1230
public:
1331
ResolverOutsideBaseError(sourcemeta::core::JSON::String uri,

src/resolver/resolver.cc

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,7 @@ auto Resolver::operator()(
188188
auto &subschema{sourcemeta::core::get(schema, entry.second.pointer)};
189189
if (subschema.is_object()) {
190190
const auto maybe_ref{subschema.try_at("$ref", ref_hash)};
191-
if (maybe_ref) {
192-
// This is safe, as at this point we have validated all schemas
193-
// against their meta-schemas
194-
assert(maybe_ref->is_string());
191+
if (maybe_ref && maybe_ref->is_string()) {
195192
normalise_ref(*result->second.collection, entry.second.base,
196193
subschema, "$ref", maybe_ref->to_string());
197194
}
@@ -200,10 +197,7 @@ auto Resolver::operator()(
200197
sourcemeta::core::SchemaBaseDialect::JSON_Schema_2020_12) {
201198
const auto maybe_dynamic_ref{
202199
subschema.try_at("$dynamicRef", dynamic_ref_hash)};
203-
if (maybe_dynamic_ref) {
204-
// This is safe, as at this point we have validated all schemas
205-
// against their meta-schemas
206-
assert(maybe_dynamic_ref->is_string());
200+
if (maybe_dynamic_ref && maybe_dynamic_ref->is_string()) {
207201
normalise_ref(*result->second.collection, entry.second.base,
208202
subschema, "$dynamicRef",
209203
maybe_dynamic_ref->to_string());
@@ -237,7 +231,9 @@ auto Resolver::add(const sourcemeta::core::JSON::String &server_url,
237231
/////////////////////////////////////////////////////////////////////////////
238232
assert(path.is_absolute());
239233
const auto schema{sourcemeta::core::read_yaml_or_json(path)};
240-
assert(sourcemeta::core::is_schema(schema));
234+
if (!sourcemeta::core::is_schema(schema)) {
235+
throw ResolverNotASchemaError(path);
236+
}
241237

242238
const std::string default_dialect_str{
243239
collection.default_dialect.value_or("")};
@@ -296,8 +292,9 @@ auto Resolver::add(const sourcemeta::core::JSON::String &server_url,
296292
/////////////////////////////////////////////////////////////////////////////
297293
const auto raw_dialect{
298294
sourcemeta::core::dialect(schema, default_dialect_str)};
299-
// If we couldn't determine the dialect, we would be in trouble!
300-
assert(!raw_dialect.empty());
295+
if (raw_dialect.empty()) {
296+
throw sourcemeta::core::SchemaUnknownDialectError();
297+
}
301298
const auto is_official_dialect{
302299
sourcemeta::core::is_known_schema(raw_dialect)};
303300
auto current_dialect{is_official_dialect

test/unit/resolver/resolver_test.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,30 @@ TEST_F(ResolverTest, entry_lookup) {
553553
"https://example.com/schemas/2020-12-with-id");
554554
}
555555

556+
TEST_F(ResolverTest, non_schema_file) {
557+
RESOLVER_INIT(resolver);
558+
try {
559+
RESOLVER_IMPORT(resolver, "example", "non-schema-array.json");
560+
FAIL();
561+
} catch (const sourcemeta::one::ResolverNotASchemaError &error) {
562+
EXPECT_EQ(error.path().filename(), "non-schema-array.json");
563+
}
564+
}
565+
566+
TEST_F(ResolverTest, no_dialect) {
567+
RESOLVER_INIT(resolver);
568+
EXPECT_THROW(RESOLVER_IMPORT(resolver, "no-base", "no-dialect.json"),
569+
sourcemeta::core::SchemaUnknownDialectError);
570+
}
571+
572+
TEST_F(ResolverTest, non_string_ref_no_crash) {
573+
RESOLVER_INIT(resolver);
574+
const auto result{
575+
RESOLVER_IMPORT(resolver, "example", "non-string-ref.json")};
576+
EXPECT_THROW(resolver(result.first.get()),
577+
sourcemeta::core::SchemaReferenceObjectResourceError);
578+
}
579+
556580
TEST_F(ResolverTest, no_base_anonymous) {
557581
RESOLVER_INIT(resolver);
558582
RESOLVER_ADD(resolver, "no-base", "anonymous.json",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[1, 2, 3]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "https://example.com/schemas/non-string-ref",
4+
"$ref": 42
5+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"$id": "https://example.com/no-dialect",
3+
"type": "string"
4+
}

0 commit comments

Comments
 (0)