From 57ce414ca81682a71288018a4d9001604002ec23 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Tue, 1 Mar 2022 11:29:26 +0100 Subject: validate repository responses against JSON schemas * compute_scripts.awk (include_file): don't enforce specific path format on #INCLUDE'd files * .gitmodules, schemas: add Haketilo JSON schemas subrepo * html/install.js (InstallView): import schema validator and run it against downloaded mapping and resource definitions * html/repo_query.js (RepoEntry): import schema validator and run it against obtained query results * test/haketilo_test/unit/test_install.py (test_install_normal_usage, test_install_dialogs): use underscore instead of hyphen in item identifiers * test/haketilo_test/unit/test_install.py (test_install_dialogs): adapt error message test cases to new handling method of invalid JSON instanced * test/haketilo_test/unit/test_repo_query.py (test_repo_query_normal_usage): use underscore instead of hyphen in item identifiers * test/haketilo_test/unit/test_repo_query.py (test_repo_query_messages): use higher sample unsupported schema version to avoid having to modify the test case soon * test/haketilo_test/world_wide_library.py: use underscore instead of hyphen in item identifiers * common/jsonschema.js, common/jsonschema: adapt tdegrunt's jsonschema and include in Haketilo, load schema documents from schemas/ --- html/install.js | 42 +++++++++++++++++------------------------- html/repo_query.js | 30 +++++++++++++++++------------- 2 files changed, 34 insertions(+), 38 deletions(-) (limited to 'html') diff --git a/html/install.js b/html/install.js index 95388a3..c502719 100644 --- a/html/install.js +++ b/html/install.js @@ -45,10 +45,11 @@ #IMPORT html/dialog.js #IMPORT html/item_preview.js AS ip -#FROM common/browser.js IMPORT browser -#FROM html/DOM_helpers.js IMPORT clone_template, Showable -#FROM common/entities.js IMPORT item_id_string, version_string, get_files -#FROM common/misc.js IMPORT sha256_async AS compute_sha256 +#FROM common/browser.js IMPORT browser +#FROM html/DOM_helpers.js IMPORT clone_template, Showable +#FROM common/entities.js IMPORT item_id_string, version_string, get_files +#FROM common/misc.js IMPORT sha256_async AS compute_sha256 +#FROM common/jsonschema.js IMPORT haketilo_validator, haketilo_schemas const coll = new Intl.Collator(); @@ -113,19 +114,6 @@ async function init_work() { new Promise((...cbs) => [work.resolve_cb, work.reject_cb] = cbs)]; } -function _make_url_reg(item_type) { - return new RegExp( - `^https://hydrilla\\.koszko\\.org/schemas/api_${item_type}_description-1\\.([1-9][0-9]*\\.)*schema\\.json$` - ); -} - -const _regexes = {}; - -function item_schema_url_regex(item_type) { - _regexes[item_type] = _regexes[item_type] || _make_url_reg(item_type); - return _regexes[item_type]; -} - function InstallView(tab_id, on_view_show, on_view_hide) { Showable.call(this, on_view_show, on_view_hide); @@ -204,18 +192,22 @@ function InstallView(tab_id, on_view_show, on_view_hide) { } const captype = item_type[0].toUpperCase() + item_type.substring(1); - const reg = item_schema_url_regex(item_type); - if (!response.json["$schema"]) { + const $id = + `https://hydrilla.koszko.org/schemas/api_${item_type}_description-1.0.1.schema.json`; + const schema = haketilo_schemas[$id]; + const result = haketilo_validator.validate(response.json, schema); + if (result.errors.length > 0) { + const reg = new RegExp(schema.allOf[2].properties.$schema.pattern); + if (response.json.$schema && !reg.test(response.json.$schema)) { + const msg = `${captype} ${item_id_string(id, ver)} was served using unsupported Hydrilla API version. You might need to update Haketilo.`; + return work.err(result.errors, msg); + } + const msg = `${captype} ${item_id_string(id, ver)} was served using a nonconforming response format.`; - return work.err(null, msg); - } else if (!reg.test(response.json["$schema"])) { - const msg = `${captype} ${item_id_string(id, ver)} was served using unsupported Hydrilla API version. You might need to update Haketilo.`; - return work.err(null, msg); + return work.err(result.errors, msg); } - /* TODO: JSON schema validation should be added here. */ - const scripts = item_type === "resource" && response.json.scripts; const files = response.json.source_copyright.concat(scripts || []); diff --git a/html/repo_query.js b/html/repo_query.js index d2f0e9b..61f4b10 100644 --- a/html/repo_query.js +++ b/html/repo_query.js @@ -43,10 +43,11 @@ #IMPORT common/indexeddb.js AS haketilodb -#FROM common/browser.js IMPORT browser -#FROM html/DOM_helpers.js IMPORT clone_template, Showable -#FROM common/entities.js IMPORT item_id_string, version_string -#FROM html/install.js IMPORT InstallView +#FROM common/browser.js IMPORT browser +#FROM html/DOM_helpers.js IMPORT clone_template, Showable +#FROM common/entities.js IMPORT item_id_string, version_string +#FROM html/install.js IMPORT InstallView +#FROM common/jsonschema.js IMPORT haketilo_validator, haketilo_schemas const coll = new Intl.Collator(); @@ -68,10 +69,6 @@ function ResultEntry(repo_entry, mapping_ref) { this.install_but.addEventListener("click", cb); } -const query_schema_url_regex = new RegExp( - "^https://hydrilla\\.koszko\\.org/schemas/api_query_result-1\\.([1-9][0-9]*\\.)*schema\\.json$" -); - function RepoEntry(query_view, repo_url) { Object.assign(this, clone_template("repo_query_single_repo")); Object.assign(this, {query_view, repo_url}); @@ -94,12 +91,19 @@ function RepoEntry(query_view, repo_url) { if ("error_json" in response) throw "Repository's response is not valid JSON :("; - if (!response.json["$schema"]) - throw "Results were served using a nonconforming response format."; - if (!query_schema_url_regex.test(response.json["$schema"])) - throw "Results were served using unsupported Hydrilla API version. You might need to update Haketilo."; + const $id = + `https://hydrilla.koszko.org/schemas/api_query_result-1.0.1.schema.json`; + const schema = haketilo_schemas[$id]; + const result = haketilo_validator.validate(response.json, schema); + if (result.errors.length > 0) { + console.error(result.errors); + + const reg = new RegExp(schema.properties.$schema.pattern); + if (response.json.$schema && !reg.test(response.json.$schema)) + throw "Results were served using unsupported Hydrilla API version. You might need to update Haketilo."; - /* TODO: here we should perform JSON schema validation! */ + throw "Results were served using a nonconforming response format."; + } return response.json.mappings; } -- cgit v1.2.3