diff --git a/RWS-Submission_Guidelines.md b/RWS-Submission_Guidelines.md
index faf210b8..a9ef8ab1 100644
--- a/RWS-Submission_Guidelines.md
+++ b/RWS-Submission_Guidelines.md
@@ -44,57 +44,7 @@ ccTLD (country code top-level domain) variants for the subsets above are also su
New submissions to the canonical RWS list must be filed as pull requests (PRs) on GitHub. Submitters should ensure that submissions follow the schema template provided below. Anyone with a GitHub account may make a submission.
Modifications to existing sets, including deletions, must also be submitted as new PRs against the canonical RWS list.
-The canonical RWS list will be validated against this schema whenever a user files their PR:
-
-```json
-{
- "type": "object",
- "properties": {
- "sets": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "contact" : {"type": "string"},
- "ccTLDs": {
- "type": "object",
- "additionalProperties": {
- "type": "array",
- "items": {
- "type": "string"
- }
- }
- },
- "primary": { "type": "string" },
- "associatedSites": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "serviceSites": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "rationaleBySite": {
- "type": "object",
- "additionalProperties": {
- "type": "string"
- }
- }
- },
- "required": ["primary"],
- "dependentRequired": {
- "associatedSites": ["An explanation of how you clearly present the affiliation across domains to users and why users would expect your domains to be affiliated"],
- "serviceSites": ["An explanation of how each domain in this subset supports functionality or security needs."]
- }
- }
- }
- }
-}
-```
+The canonical RWS list will be validated against [this schema](./SCHEMA.json) whenever a user files their PR.
A hypothetical example of the RWS canonical list is provided below for reference. A submission should follow the structure below, with new submissions being added as items to the "sets" list.
diff --git a/SCHEMA.json b/SCHEMA.json
index 388ae13b..fc54d62c 100644
--- a/SCHEMA.json
+++ b/SCHEMA.json
@@ -1,11 +1,13 @@
{
"type": "object",
+ "additionalProperties": false,
"properties": {
- "contact" : {"type": "string"},
"sets": {
"type": "array",
+ "uniqueItems": true,
"items": {
"type": "object",
+ "additionalProperties": false,
"properties": {
"ccTLDs": {
"type": "object",
@@ -16,15 +18,18 @@
}
}
},
+ "contact": { "type": "string" },
"primary": {"type": "string"},
"associatedSites": {
"type": "array",
+ "uniqueItems": true,
"items": {
"type": "string"
}
},
"serviceSites": {
"type": "array",
+ "uniqueItems": true,
"items": {
"type": "string"
}
@@ -40,8 +45,13 @@
"dependentRequired": {
"associatedSites": ["rationaleBySite"],
"serviceSites": ["rationaleBySite"]
- }
+ },
+ "anyOf": [
+ { "required": ["associatedSites"] },
+ { "required": ["serviceSites"] },
+ { "required": ["ccTLDs"] }
+ ]
}
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/rws_tests.py b/tests/rws_tests.py
index 429d39ca..5c6c7d90 100644
--- a/tests/rws_tests.py
+++ b/tests/rws_tests.py
@@ -82,6 +82,87 @@ def test_parse_and_check_format(self):
class TestValidateSchema(unittest.TestCase):
"""A test suite for the validate_schema function of RwsCheck"""
+ def test_valid_associated(self):
+ json_dict = {
+ "sets": [
+ {
+ "contact": "abc@example.com",
+ "primary": "https://primary.com",
+ "associatedSites": ["https://associated1.com"],
+ "rationaleBySite": {
+ "https://associated1.com": "example rationale",
+ },
+ }
+ ]
+ }
+ rws_check = RwsCheck(rws_sites=json_dict, etlds=None, icanns=set(["ca"]))
+ rws_check.validate_schema("SCHEMA.json")
+
+ def test_valid_service(self):
+ json_dict = {
+ "sets": [
+ {
+ "contact": "abc@example.com",
+ "primary": "https://primary.com",
+ "serviceSites": ["https://service1.com"],
+ "rationaleBySite": {
+ "https://service1.com": "example rationale",
+ },
+ }
+ ]
+ }
+ rws_check = RwsCheck(rws_sites=json_dict, etlds=None, icanns=set(["ca"]))
+ rws_check.validate_schema("SCHEMA.json")
+
+ def test_valid_ccTLDs(self):
+ json_dict = {
+ "sets": [
+ {
+ "contact": "abc@example.com",
+ "primary": "https://primary.com",
+ "ccTLDs": {"https://primary.com": ["https://primary.ca"]},
+ }
+ ]
+ }
+ rws_check = RwsCheck(rws_sites=json_dict, etlds=None, icanns=set(["ca"]))
+ rws_check.validate_schema("SCHEMA.json")
+
+ def test_valid_full(self):
+ json_dict = {
+ "sets": [
+ {
+ "contact": "abc@example.com",
+ "primary": "https://primary.com",
+ "associatedSites": ["https://associated1.com"],
+ "serviceSites": ["https://service1.com"],
+ "rationaleBySite": {
+ "https://associated1.com": "example rationale",
+ "https://service1.com": "example rationale",
+ },
+ "ccTLDs": {"https://associated1.com": ["https://associated1.ca"]},
+ }
+ ]
+ }
+ rws_check = RwsCheck(rws_sites=json_dict, etlds=None, icanns=set(["ca"]))
+ rws_check.validate_schema("SCHEMA.json")
+
+ def test_duplicate_set(self):
+ entry = {
+ "contact": "abc@example.com",
+ "primary": "https://primary.com",
+ "associatedSites": ["https://associated1.com"],
+ "serviceSites": ["https://service1.com"],
+ "rationaleBySite": {
+ "https://associated1.com": "example rationale",
+ "https://service1.com": "example rationale",
+ },
+ "ccTLDs": {"https://associated1.com": ["https://associated1.ca"]},
+ }
+ json_dict = {"sets": [entry, entry]}
+ rws_check = RwsCheck(rws_sites=json_dict, etlds=None, icanns=set(["ca"]))
+ with self.assertRaises(ValidationError):
+ rws_check.validate_schema("SCHEMA.json")
+
def test_no_primary(self):
json_dict = {
"sets": [
@@ -101,6 +182,19 @@ def test_no_primary(self):
with self.assertRaises(ValidationError):
rws_check.validate_schema("SCHEMA.json")
+ def test_primary_only(self):
+ json_dict = {
+ "sets": [
+ {
+ "contact": "abc@example.com",
+ "primary": "https://primary.com",
+ }
+ ]
+ }
+ rws_check = RwsCheck(rws_sites=json_dict, etlds=None, icanns=set(["ca"]))
+ with self.assertRaises(ValidationError):
+ rws_check.validate_schema("SCHEMA.json")
+
def test_no_rationaleBySite(self):
json_dict = {
"sets": [
@@ -150,6 +244,71 @@ def test_no_contact(self):
with self.assertRaises(ValidationError):
rws_check.validate_schema("SCHEMA.json")
+ def test_nonunique_associated_sites(self):
+ json_dict = {
+ "sets": [
+ {
+ "contact": "abc@example.com",
+ "primary": "https://primary.com",
+ "associatedSites": [
+ "https://associated1.com",
+ "https://associated1.com",
+ ],
+ "rationaleBySite": {
+ "https://associated1.com": "example rationale",
+ },
+ }
+ ]
+ }
+ rws_check = RwsCheck(rws_sites=json_dict, etlds=None, icanns=set(["ca"]))
+ with self.assertRaises(ValidationError):
+ rws_check.validate_schema("SCHEMA.json")
+
+ def test_nonunique_service_sites(self):
+ json_dict = {
+ "sets": [
+ {
+ "contact": "abc@example.com",
+ "primary": "https://primary.com",
+ "serviceSites": [
+ "https://service1.com",
+ "https://service1.com",
+ ],
+ "rationaleBySite": {
+ "https://service1.com": "example rationale",
+ },
+ }
+ ]
+ }
+ rws_check = RwsCheck(rws_sites=json_dict, etlds=None, icanns=set(["ca"]))
+ with self.assertRaises(ValidationError):
+ rws_check.validate_schema("SCHEMA.json")
+
+ def test_unexpected_top_level_property(self):
+ json_dict = {"sets": [], "foo": True}
+ rws_check = RwsCheck(rws_sites=json_dict, etlds=None, icanns=set(["ca"]))
+ with self.assertRaises(ValidationError):
+ rws_check.validate_schema("SCHEMA.json")
+
+ def test_unexpected_set_level_property(self):
+ json_dict = {
+ "sets": [
+ {
+ "contact": "abc@example.com",
+ "primary": "https://primary.com",
+ "associatedSites": ["https://associated1.com"],
+ "rationaleBySite": {
+ "https://associated1.com": "example rationale",
+ },
+ "ccTLDs": {"https://associated1.com": ["https://associated1.ca"]},
+ "foo": True,
+ }
+ ]
+ }
+ rws_check = RwsCheck(rws_sites=json_dict, etlds=None, icanns=set(["ca"]))
+ with self.assertRaises(ValidationError):
+ rws_check.validate_schema("SCHEMA.json")
+
class TestRwsSetEqual(unittest.TestCase):
def test_equal_case(self):