Skip to content

Commit 30f46ee

Browse files
authored
Lint wrong scheme in default dialect URIs (#2334)
See: sourcemeta/jsonschema#707 Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent 3caccc7 commit 30f46ee

13 files changed

+496
-0
lines changed

src/extension/alterschema/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME alterschema
3131
common/content_schema_without_media_type.h
3232
common/dependencies_property_tautology.h
3333
common/dependent_required_tautology.h
34+
common/draft_official_dialect_with_https.h
3435
common/draft_official_dialect_without_empty_fragment.h
3536
common/draft_ref_siblings.h
3637
common/drop_allof_empty_schemas.h
@@ -52,6 +53,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME alterschema
5253
common/min_contains_without_contains.h
5354
common/minimum_real_for_integer.h
5455
common/modern_official_dialect_with_empty_fragment.h
56+
common/modern_official_dialect_with_http.h
5557
common/non_applicable_additional_items.h
5658
common/non_applicable_enum_validation_keywords.h
5759
common/non_applicable_type_specific_keywords.h

src/extension/alterschema/alterschema.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ inline auto APPLIES_TO_POINTERS(std::vector<Pointer> &&keywords)
5959
#include "common/content_schema_without_media_type.h"
6060
#include "common/dependencies_property_tautology.h"
6161
#include "common/dependent_required_tautology.h"
62+
#include "common/draft_official_dialect_with_https.h"
6263
#include "common/draft_official_dialect_without_empty_fragment.h"
6364
#include "common/draft_ref_siblings.h"
6465
#include "common/drop_allof_empty_schemas.h"
@@ -80,6 +81,7 @@ inline auto APPLIES_TO_POINTERS(std::vector<Pointer> &&keywords)
8081
#include "common/min_contains_without_contains.h"
8182
#include "common/minimum_real_for_integer.h"
8283
#include "common/modern_official_dialect_with_empty_fragment.h"
84+
#include "common/modern_official_dialect_with_http.h"
8385
#include "common/non_applicable_additional_items.h"
8486
#include "common/non_applicable_enum_validation_keywords.h"
8587
#include "common/non_applicable_type_specific_keywords.h"
@@ -149,6 +151,7 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) -> void {
149151

150152
bundle.add<ContentMediaTypeWithoutEncoding>();
151153
bundle.add<ContentSchemaWithoutMediaType>();
154+
bundle.add<DraftOfficialDialectWithHttps>();
152155
bundle.add<DraftOfficialDialectWithoutEmptyFragment>();
153156
bundle.add<NonApplicableTypeSpecificKeywords>();
154157
bundle.add<AnyOfRemoveFalseSchemas>();
@@ -184,6 +187,7 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) -> void {
184187
bundle.add<ConstInEnum>();
185188
bundle.add<NonApplicableAdditionalItems>();
186189
bundle.add<ModernOfficialDialectWithEmptyFragment>();
190+
bundle.add<ModernOfficialDialectWithHttp>();
187191
bundle.add<ExclusiveMaximumNumberAndMaximum>();
188192
bundle.add<ExclusiveMinimumNumberAndMinimum>();
189193
bundle.add<DraftRefSiblings>();
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
class DraftOfficialDialectWithHttps final : public SchemaTransformRule {
2+
public:
3+
using mutates = std::true_type;
4+
using reframe_after_transform = std::true_type;
5+
DraftOfficialDialectWithHttps()
6+
: SchemaTransformRule{
7+
"draft_official_dialect_with_https",
8+
"The official dialect URI of Draft 7 and older must use "
9+
"\"http://\" instead of \"https://\""} {};
10+
11+
[[nodiscard]] auto
12+
condition(const sourcemeta::core::JSON &schema,
13+
const sourcemeta::core::JSON &,
14+
const sourcemeta::core::Vocabularies &,
15+
const sourcemeta::core::SchemaFrame &,
16+
const sourcemeta::core::SchemaFrame::Location &location,
17+
const sourcemeta::core::SchemaWalker &,
18+
const sourcemeta::core::SchemaResolver &) const
19+
-> sourcemeta::core::SchemaTransformRule::Result override {
20+
using sourcemeta::core::SchemaBaseDialect;
21+
ONLY_CONTINUE_IF(
22+
location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_7 ||
23+
location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_7_Hyper ||
24+
location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_6 ||
25+
location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_6_Hyper ||
26+
location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_4 ||
27+
location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_4_Hyper ||
28+
location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_3 ||
29+
location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_3_Hyper ||
30+
location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_2_Hyper ||
31+
location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_1_Hyper ||
32+
location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_0_Hyper);
33+
ONLY_CONTINUE_IF(schema.is_object() && schema.defines("$schema") &&
34+
schema.at("$schema").is_string());
35+
const auto &dialect{schema.at("$schema").to_string()};
36+
ONLY_CONTINUE_IF(dialect.starts_with("https://json-schema.org/"));
37+
ONLY_CONTINUE_IF(
38+
dialect == "https://json-schema.org/draft-07/schema" ||
39+
dialect == "https://json-schema.org/draft-07/schema#" ||
40+
dialect == "https://json-schema.org/draft-07/hyper-schema" ||
41+
dialect == "https://json-schema.org/draft-07/hyper-schema#" ||
42+
dialect == "https://json-schema.org/draft-06/schema" ||
43+
dialect == "https://json-schema.org/draft-06/schema#" ||
44+
dialect == "https://json-schema.org/draft-06/hyper-schema" ||
45+
dialect == "https://json-schema.org/draft-06/hyper-schema#" ||
46+
dialect == "https://json-schema.org/draft-04/schema" ||
47+
dialect == "https://json-schema.org/draft-04/schema#" ||
48+
dialect == "https://json-schema.org/draft-04/hyper-schema" ||
49+
dialect == "https://json-schema.org/draft-04/hyper-schema#" ||
50+
dialect == "https://json-schema.org/draft-03/schema" ||
51+
dialect == "https://json-schema.org/draft-03/schema#" ||
52+
dialect == "https://json-schema.org/draft-03/hyper-schema" ||
53+
dialect == "https://json-schema.org/draft-03/hyper-schema#" ||
54+
dialect == "https://json-schema.org/draft-02/schema" ||
55+
dialect == "https://json-schema.org/draft-02/schema#" ||
56+
dialect == "https://json-schema.org/draft-02/hyper-schema" ||
57+
dialect == "https://json-schema.org/draft-02/hyper-schema#" ||
58+
dialect == "https://json-schema.org/draft-01/schema" ||
59+
dialect == "https://json-schema.org/draft-01/schema#" ||
60+
dialect == "https://json-schema.org/draft-01/hyper-schema" ||
61+
dialect == "https://json-schema.org/draft-01/hyper-schema#" ||
62+
dialect == "https://json-schema.org/draft-00/schema" ||
63+
dialect == "https://json-schema.org/draft-00/schema#" ||
64+
dialect == "https://json-schema.org/draft-00/hyper-schema" ||
65+
dialect == "https://json-schema.org/draft-00/hyper-schema#");
66+
return APPLIES_TO_KEYWORDS("$schema");
67+
}
68+
69+
auto transform(sourcemeta::core::JSON &schema, const Result &) const
70+
-> void override {
71+
const auto &old_dialect{schema.at("$schema").to_string()};
72+
std::string new_dialect{"http://"};
73+
new_dialect += old_dialect.substr(8);
74+
schema.at("$schema").into(sourcemeta::core::JSON{new_dialect});
75+
}
76+
};
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
class ModernOfficialDialectWithHttp final : public SchemaTransformRule {
2+
public:
3+
using mutates = std::true_type;
4+
using reframe_after_transform = std::true_type;
5+
ModernOfficialDialectWithHttp()
6+
: SchemaTransformRule{
7+
"modern_official_dialect_with_http",
8+
"The official dialect URI of 2019-09 and later must use "
9+
"\"https://\" instead of \"http://\""} {};
10+
11+
[[nodiscard]] auto
12+
condition(const sourcemeta::core::JSON &schema,
13+
const sourcemeta::core::JSON &,
14+
const sourcemeta::core::Vocabularies &,
15+
const sourcemeta::core::SchemaFrame &,
16+
const sourcemeta::core::SchemaFrame::Location &location,
17+
const sourcemeta::core::SchemaWalker &,
18+
const sourcemeta::core::SchemaResolver &) const
19+
-> sourcemeta::core::SchemaTransformRule::Result override {
20+
using sourcemeta::core::SchemaBaseDialect;
21+
ONLY_CONTINUE_IF(
22+
location.base_dialect == SchemaBaseDialect::JSON_Schema_2020_12 ||
23+
location.base_dialect == SchemaBaseDialect::JSON_Schema_2020_12_Hyper ||
24+
location.base_dialect == SchemaBaseDialect::JSON_Schema_2019_09 ||
25+
location.base_dialect == SchemaBaseDialect::JSON_Schema_2019_09_Hyper);
26+
ONLY_CONTINUE_IF(schema.is_object() && schema.defines("$schema") &&
27+
schema.at("$schema").is_string());
28+
const auto &dialect{schema.at("$schema").to_string()};
29+
ONLY_CONTINUE_IF(dialect.starts_with("http://json-schema.org/"));
30+
ONLY_CONTINUE_IF(
31+
dialect == "http://json-schema.org/draft/2020-12/schema" ||
32+
dialect == "http://json-schema.org/draft/2020-12/schema#" ||
33+
dialect == "http://json-schema.org/draft/2020-12/hyper-schema" ||
34+
dialect == "http://json-schema.org/draft/2020-12/hyper-schema#" ||
35+
dialect == "http://json-schema.org/draft/2019-09/schema" ||
36+
dialect == "http://json-schema.org/draft/2019-09/schema#" ||
37+
dialect == "http://json-schema.org/draft/2019-09/hyper-schema" ||
38+
dialect == "http://json-schema.org/draft/2019-09/hyper-schema#");
39+
return APPLIES_TO_KEYWORDS("$schema");
40+
}
41+
42+
auto transform(sourcemeta::core::JSON &schema, const Result &) const
43+
-> void override {
44+
const auto &old_dialect{schema.at("$schema").to_string()};
45+
std::string new_dialect{"https://"};
46+
new_dialect += old_dialect.substr(7);
47+
schema.at("$schema").into(sourcemeta::core::JSON{new_dialect});
48+
}
49+
};

test/alterschema/alterschema_lint_2019_09_test.cc

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4811,3 +4811,76 @@ TEST(AlterSchema_lint_2019_09, invalid_external_ref_2) {
48114811
EXPECT_TRUE(result.first);
48124812
EXPECT_EQ(traces.size(), 0);
48134813
}
4814+
4815+
TEST(AlterSchema_lint_2019_09, modern_official_dialect_with_http_1) {
4816+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
4817+
"$schema": "http://json-schema.org/draft/2019-09/schema",
4818+
"type": "string"
4819+
})JSON");
4820+
4821+
LINT_AND_FIX(document, result, traces);
4822+
4823+
EXPECT_FALSE(result.first);
4824+
4825+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
4826+
"$schema": "https://json-schema.org/draft/2019-09/schema",
4827+
"type": "string"
4828+
})JSON");
4829+
4830+
EXPECT_EQ(document, expected);
4831+
}
4832+
4833+
TEST(AlterSchema_lint_2019_09, modern_official_dialect_with_http_2) {
4834+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
4835+
"$schema": "http://json-schema.org/draft/2019-09/hyper-schema",
4836+
"type": "string"
4837+
})JSON");
4838+
4839+
LINT_AND_FIX(document, result, traces);
4840+
4841+
EXPECT_FALSE(result.first);
4842+
4843+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
4844+
"$schema": "https://json-schema.org/draft/2019-09/hyper-schema",
4845+
"type": "string"
4846+
})JSON");
4847+
4848+
EXPECT_EQ(document, expected);
4849+
}
4850+
4851+
TEST(AlterSchema_lint_2019_09, modern_official_dialect_with_http_3) {
4852+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
4853+
"$schema": "http://json-schema.org/draft/2019-09/schema#",
4854+
"type": "string"
4855+
})JSON");
4856+
4857+
LINT_AND_FIX(document, result, traces);
4858+
4859+
EXPECT_FALSE(result.first);
4860+
4861+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
4862+
"$schema": "https://json-schema.org/draft/2019-09/schema",
4863+
"type": "string"
4864+
})JSON");
4865+
4866+
EXPECT_EQ(document, expected);
4867+
}
4868+
4869+
TEST(AlterSchema_lint_2019_09,
4870+
modern_official_dialect_with_http_already_https) {
4871+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
4872+
"$schema": "https://json-schema.org/draft/2019-09/schema",
4873+
"type": "string"
4874+
})JSON");
4875+
4876+
LINT_AND_FIX(document, result, traces);
4877+
4878+
EXPECT_FALSE(result.first);
4879+
4880+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
4881+
"$schema": "https://json-schema.org/draft/2019-09/schema",
4882+
"type": "string"
4883+
})JSON");
4884+
4885+
EXPECT_EQ(document, expected);
4886+
}

test/alterschema/alterschema_lint_2020_12_test.cc

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10354,3 +10354,95 @@ TEST(AlterSchema_lint_2020_12, invalid_external_ref_11) {
1035410354
"resolved",
1035510355
false);
1035610356
}
10357+
10358+
TEST(AlterSchema_lint_2020_12, modern_official_dialect_with_http_1) {
10359+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
10360+
"$schema": "http://json-schema.org/draft/2020-12/schema",
10361+
"type": "string"
10362+
})JSON");
10363+
10364+
LINT_AND_FIX(document, result, traces);
10365+
10366+
EXPECT_FALSE(result.first);
10367+
10368+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
10369+
"$schema": "https://json-schema.org/draft/2020-12/schema",
10370+
"type": "string"
10371+
})JSON");
10372+
10373+
EXPECT_EQ(document, expected);
10374+
}
10375+
10376+
TEST(AlterSchema_lint_2020_12, modern_official_dialect_with_http_2) {
10377+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
10378+
"$schema": "http://json-schema.org/draft/2020-12/hyper-schema",
10379+
"type": "string"
10380+
})JSON");
10381+
10382+
LINT_AND_FIX(document, result, traces);
10383+
10384+
EXPECT_FALSE(result.first);
10385+
10386+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
10387+
"$schema": "https://json-schema.org/draft/2020-12/hyper-schema",
10388+
"type": "string"
10389+
})JSON");
10390+
10391+
EXPECT_EQ(document, expected);
10392+
}
10393+
10394+
TEST(AlterSchema_lint_2020_12, modern_official_dialect_with_http_3) {
10395+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
10396+
"$schema": "http://json-schema.org/draft/2020-12/schema#",
10397+
"type": "string"
10398+
})JSON");
10399+
10400+
LINT_AND_FIX(document, result, traces);
10401+
10402+
EXPECT_FALSE(result.first);
10403+
10404+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
10405+
"$schema": "https://json-schema.org/draft/2020-12/schema",
10406+
"type": "string"
10407+
})JSON");
10408+
10409+
EXPECT_EQ(document, expected);
10410+
}
10411+
10412+
TEST(AlterSchema_lint_2020_12,
10413+
modern_official_dialect_with_http_already_https) {
10414+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
10415+
"$schema": "https://json-schema.org/draft/2020-12/schema",
10416+
"type": "string"
10417+
})JSON");
10418+
10419+
LINT_AND_FIX(document, result, traces);
10420+
10421+
EXPECT_FALSE(result.first);
10422+
10423+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
10424+
"$schema": "https://json-schema.org/draft/2020-12/schema",
10425+
"type": "string"
10426+
})JSON");
10427+
10428+
EXPECT_EQ(document, expected);
10429+
}
10430+
10431+
TEST(AlterSchema_lint_2020_12,
10432+
modern_official_dialect_with_http_non_dialect_uri) {
10433+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
10434+
"$schema": "https://json-schema.org/draft/2020-12/schema",
10435+
"$ref": "http://json-schema.org/draft/2020-12/meta/applicator"
10436+
})JSON");
10437+
10438+
LINT_AND_FIX(document, result, traces);
10439+
10440+
EXPECT_FALSE(result.first);
10441+
10442+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
10443+
"$schema": "https://json-schema.org/draft/2020-12/schema",
10444+
"$ref": "http://json-schema.org/draft/2020-12/meta/applicator"
10445+
})JSON");
10446+
10447+
EXPECT_EQ(document, expected);
10448+
}

test/alterschema/alterschema_lint_draft0_test.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,3 +522,21 @@ TEST(AlterSchema_lint_draft0, unknown_keywords_prefix_10) {
522522

523523
EXPECT_EQ(document, expected);
524524
}
525+
526+
TEST(AlterSchema_lint_draft0, draft_official_dialect_with_https_1) {
527+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
528+
"$schema": "https://json-schema.org/draft-00/schema#",
529+
"type": "string"
530+
})JSON");
531+
532+
LINT_AND_FIX(document, result, traces);
533+
534+
EXPECT_TRUE(result.first);
535+
536+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
537+
"$schema": "http://json-schema.org/draft-00/schema#",
538+
"type": "string"
539+
})JSON");
540+
541+
EXPECT_EQ(document, expected);
542+
}

test/alterschema/alterschema_lint_draft1_test.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,3 +1095,21 @@ TEST(AlterSchema_lint_draft1, unknown_keywords_prefix_10) {
10951095

10961096
EXPECT_EQ(document, expected);
10971097
}
1098+
1099+
TEST(AlterSchema_lint_draft1, draft_official_dialect_with_https_1) {
1100+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
1101+
"$schema": "https://json-schema.org/draft-01/schema#",
1102+
"type": "string"
1103+
})JSON");
1104+
1105+
LINT_AND_FIX(document, result, traces);
1106+
1107+
EXPECT_FALSE(result.first);
1108+
1109+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
1110+
"$schema": "http://json-schema.org/draft-01/schema#",
1111+
"type": "string"
1112+
})JSON");
1113+
1114+
EXPECT_EQ(document, expected);
1115+
}

0 commit comments

Comments
 (0)