From 4f4b83d1b8eb8be3c69006bf3153c894cf67427b Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Wed, 24 Jun 2026 10:20:33 -0400 Subject: [PATCH] Fix YAML syntax errors not properly caught Signed-off-by: Juan Cruz Viotti --- src/error.h | 5 +++ src/input.h | 6 ++- src/resolver.h | 6 ++- test/CMakeLists.txt | 3 ++ test/codegen/fail_schema_invalid_yaml.sh | 45 +++++++++++++++++++++ test/inspect/fail_schema_invalid_yaml.sh | 45 +++++++++++++++++++++ test/lint/fail_lint_rule_invalid_yaml.sh | 51 ++++++++++++++++++++++++ 7 files changed, 159 insertions(+), 2 deletions(-) create mode 100755 test/codegen/fail_schema_invalid_yaml.sh create mode 100755 test/inspect/fail_schema_invalid_yaml.sh create mode 100755 test/lint/fail_lint_rule_invalid_yaml.sh diff --git a/src/error.h b/src/error.h index d9ba3afe..b313a866 100644 --- a/src/error.h +++ b/src/error.h @@ -14,6 +14,7 @@ #include #include #include +#include #include // std::same_as, std::convertible_to #include // std::uint64_t @@ -1009,6 +1010,10 @@ inline auto try_catch(const sourcemeta::core::Options &options, const auto is_json{options.contains("json")}; print_exception(is_json, error); return EXIT_NOT_SUPPORTED; + } catch (const sourcemeta::core::YAMLFileParseError &error) { + const auto is_json{options.contains("json")}; + print_exception(is_json, error); + return EXIT_OTHER_INPUT_ERROR; } catch (const sourcemeta::core::JSONFileParseError &error) { const auto is_json{options.contains("json")}; print_exception(is_json, error); diff --git a/src/input.h b/src/input.h index c7c9bc93..2e35722b 100644 --- a/src/input.h +++ b/src/input.h @@ -369,7 +369,11 @@ handle_json_entry(const std::filesystem::path &entry_path, index, property_storage->back()); }; sourcemeta::core::JSON document{sourcemeta::core::JSON{nullptr}}; - sourcemeta::core::parse_yaml(stream, document, callback); + try { + sourcemeta::core::parse_yaml(stream, document, callback); + } catch (const sourcemeta::core::YAMLParseError &error) { + throw sourcemeta::core::YAMLFileParseError{canonical, error}; + } documents.push_back({std::move(document), std::move(positions), std::move(property_storage)}); line_offset += max_line > 0 ? max_line - 1 : 0; diff --git a/src/resolver.h b/src/resolver.h index c5684bd1..60720f73 100644 --- a/src/resolver.h +++ b/src/resolver.h @@ -178,7 +178,11 @@ static inline auto http_fetch(const std::string &url, sourcemeta::core::http_header_find(response.headers, "content-type")}; if (content_type.has_value() && sourcemeta::core::http_content_type_matches( content_type.value(), "text/yaml")) { - return sourcemeta::core::parse_yaml(response.body); + try { + return sourcemeta::core::parse_yaml(response.body); + } catch (const sourcemeta::core::YAMLParseError &error) { + throw sourcemeta::core::YAMLFileParseError{url, error}; + } } return sourcemeta::core::parse_json(response.body); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dd32909e..6cf18a2a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -551,6 +551,7 @@ add_jsonschema_test_unix(inspect/pass_yaml) add_jsonschema_test_unix(inspect/pass_json_output) add_jsonschema_test_unix(inspect/fail_no_schema) add_jsonschema_test_unix(inspect/fail_schema_invalid_json) +add_jsonschema_test_unix(inspect/fail_schema_invalid_yaml) add_jsonschema_test_unix(inspect/fail_unknown_metaschema) add_jsonschema_test_unix(inspect/fail_default_dialect_config_extension_mismatch) add_jsonschema_test_unix(inspect/fail_relative_file_metaschema_ref) @@ -640,6 +641,7 @@ add_jsonschema_test_unix(codegen/fail_no_schema) add_jsonschema_test_unix(codegen/fail_missing_target) add_jsonschema_test_unix(codegen/fail_invalid_target) add_jsonschema_test_unix(codegen/fail_schema_invalid_json) +add_jsonschema_test_unix(codegen/fail_schema_invalid_yaml) add_jsonschema_test_unix(codegen/fail_unknown_metaschema) add_jsonschema_test_unix(codegen/fail_default_dialect_config_extension_mismatch) add_jsonschema_test_unix(codegen/fail_unsupported_vocabulary) @@ -783,6 +785,7 @@ add_jsonschema_test_unix(lint/fail_lint_rule_duplicate_builtin) add_jsonschema_test_unix(lint/fail_lint_rule_duplicate_custom) add_jsonschema_test_unix(lint/fail_lint_rule_invalid_json) add_jsonschema_test_unix(lint/fail_lint_rule_unresolvable) +add_jsonschema_test_unix(lint/fail_lint_rule_invalid_yaml) add_jsonschema_test_unix(lint/fail_lint_rule_property_names) add_jsonschema_test_unix(lint/fail_lint_rule_property_names_json) add_jsonschema_test_unix(lint/pass_lint_config_rule_no_violation) diff --git a/test/codegen/fail_schema_invalid_yaml.sh b/test/codegen/fail_schema_invalid_yaml.sh new file mode 100755 index 00000000..ae7c8030 --- /dev/null +++ b/test/codegen/fail_schema_invalid_yaml.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +set -o errexit +set -o nounset + +TMP="$(mktemp -d)" +clean() { rm -rf "$TMP"; } +trap clean EXIT + +cat << 'EOF' > "$TMP/schema.yaml" +type: string +--- +type: integer +EOF + +"$1" codegen "$TMP/schema.yaml" --target typescript 2> "$TMP/stderr.txt" \ + && EXIT_CODE="$?" || EXIT_CODE="$?" +# Other input error +test "$EXIT_CODE" = "6" + +cat << EOF > "$TMP/expected.txt" +error: Unexpected content after document + at line 3 + at column 1 + at file path $(realpath "$TMP")/schema.yaml +EOF + +diff "$TMP/stderr.txt" "$TMP/expected.txt" + +# JSON error +"$1" codegen "$TMP/schema.yaml" --target typescript --json > "$TMP/stdout.txt" \ + && EXIT_CODE="$?" || EXIT_CODE="$?" +# Other input error +test "$EXIT_CODE" = "6" + +cat << EOF > "$TMP/expected.txt" +{ + "error": "Unexpected content after document", + "line": 3, + "column": 1, + "filePath": "$(realpath "$TMP")/schema.yaml" +} +EOF + +diff "$TMP/stdout.txt" "$TMP/expected.txt" diff --git a/test/inspect/fail_schema_invalid_yaml.sh b/test/inspect/fail_schema_invalid_yaml.sh new file mode 100755 index 00000000..9bcc516e --- /dev/null +++ b/test/inspect/fail_schema_invalid_yaml.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +set -o errexit +set -o nounset + +TMP="$(mktemp -d)" +clean() { rm -rf "$TMP"; } +trap clean EXIT + +cat << 'EOF' > "$TMP/schema.yaml" +type: string +--- +type: integer +EOF + +"$1" inspect "$TMP/schema.yaml" 2> "$TMP/stderr.txt" \ + && EXIT_CODE="$?" || EXIT_CODE="$?" +# Other input error +test "$EXIT_CODE" = "6" + +cat << EOF > "$TMP/expected.txt" +error: Unexpected content after document + at line 3 + at column 1 + at file path $(realpath "$TMP")/schema.yaml +EOF + +diff "$TMP/stderr.txt" "$TMP/expected.txt" + +# JSON error +"$1" inspect "$TMP/schema.yaml" --json > "$TMP/stdout.txt" \ + && EXIT_CODE="$?" || EXIT_CODE="$?" +# Other input error +test "$EXIT_CODE" = "6" + +cat << EOF > "$TMP/expected.txt" +{ + "error": "Unexpected content after document", + "line": 3, + "column": 1, + "filePath": "$(realpath "$TMP")/schema.yaml" +} +EOF + +diff "$TMP/stdout.txt" "$TMP/expected.txt" diff --git a/test/lint/fail_lint_rule_invalid_yaml.sh b/test/lint/fail_lint_rule_invalid_yaml.sh new file mode 100755 index 00000000..c2113fc5 --- /dev/null +++ b/test/lint/fail_lint_rule_invalid_yaml.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +set -o errexit +set -o nounset + +TMP="$(mktemp -d)" +clean() { rm -rf "$TMP"; } +trap clean EXIT + +cat << 'EOF' > "$TMP/rule.yaml" +title: require_type +--- +title: another +EOF + +cat << 'EOF' > "$TMP/schema.json" +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string" +} +EOF + +"$1" lint --rule "$TMP/rule.yaml" "$TMP/schema.json" \ + > "$TMP/output.txt" 2>&1 && EXIT_CODE="$?" || EXIT_CODE="$?" +# Other input error +test "$EXIT_CODE" = "6" + +cat << EOF > "$TMP/expected.txt" +error: Unexpected content after document + at line 3 + at column 1 + at file path $(realpath "$TMP")/rule.yaml +EOF + +diff "$TMP/output.txt" "$TMP/expected.txt" + +"$1" lint --rule "$TMP/rule.yaml" --json "$TMP/schema.json" \ + > "$TMP/output_json.txt" 2>&1 && EXIT_CODE="$?" || EXIT_CODE="$?" +# Other input error +test "$EXIT_CODE" = "6" + +cat << EOF > "$TMP/expected_json.txt" +{ + "error": "Unexpected content after document", + "line": 3, + "column": 1, + "filePath": "$(realpath "$TMP")/rule.yaml" +} +EOF + +diff "$TMP/output_json.txt" "$TMP/expected_json.txt"