/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** * A facade around @cfworker/json-schema that provides additional formats and * convenience methods whil executing inside a sandbox. */ const sandbox = new Cu.Sandbox(null, { wantComponents: false, wantGlobalProperties: ["URL"], }); Services.scriptloader.loadSubScript( "chrome://global/content/third_party/cfworker/json-schema.js", sandbox ); /** * A JSON Schema string format for URLs intended to go through Services.urlFormatter. */ Cu.exportFunction( function validateMozUrlFormat(input) { try { const formatted = Services.urlFormatter.formatURL(input); return Cu.waiveXrays(sandbox.fastFormat).uri(formatted); } catch { return false; } }, sandbox.fastFormat, { defineAs: "moz-url-format" } ); // initialBaseURI defaults to github.com/cfworker, which will be confusing. Cu.evalInSandbox( `this.initialBaseURI = initialBaseURI = new URL("http://mozilla.org");`, sandbox ); /** * A JSONSchema validator that performs validation inside a sandbox. */ class Validator { #inner; #draft; /** * Create a new validator. * * @param {object} schema The schema to validate with. * @param {object} options Options for the validator. * @param {string} options.draft The draft to validate against. Should be one * of "4", "6", "7", "2019-09", or "2020-12". * * If the |$schema| key is present in the * |schema|, it will be used to auto-detect the * correct version. Otherwise, 2019-09 will be * used. * @param {boolean} options.shortCircuit Whether or not the validator should * return after a single error occurs. */ constructor( schema, { draft = detectSchemaDraft(schema), shortCircuit = true } = {} ) { this.#draft = draft; this.#inner = Cu.waiveXrays( new sandbox.Validator(Cu.cloneInto(schema, sandbox), draft, shortCircuit) ); } /** * Validate the instance against the known schemas. * * @param {object} instance The instance to validate. * * @return {object} An object with |valid| and |errors| keys that indicates * the success of validation. */ validate(instance) { return this.#inner.validate(Cu.cloneInto(instance, sandbox)); } /** * Add a schema to the validator. * * @param {object} schema A JSON schema object. * @param {string} id An optional ID to identify the schema if it does not * provide an |$id| field. */ addSchema(schema, id) { const draft = detectSchemaDraft(schema, undefined); if (draft && this.#draft != draft) { console.error( `Adding a draft "${draft}" schema to a draft "${ this.#draft }" validator.` ); } this.#inner.addSchema(Cu.cloneInto(schema, sandbox), id); } } /** * A wrapper around validate that provides some options as an object * instead of positional arguments. * * @param {object} instance The instance to validate. * @param {object} schema The JSON schema to validate against. * @param {object} options Options for the validator. * @param {string} options.draft The draft to validate against. Should * be one of "4", "6", "7", "2019-09", or "2020-12". * * If the |$schema| key is present in the |schema|, it * will be used to auto-detect the correct version. * Otherwise, 2019-09 will be used. * @param {boolean} options.shortCircuit Whether or not the validator should * return after a single error occurs. * * @returns {object} An object with |valid| and |errors| keys that indicates the * success of validation. */ function validate( instance, schema, { draft = detectSchemaDraft(schema), shortCircuit = true } = {} ) { const clonedSchema = Cu.cloneInto(schema, sandbox); return sandbox.validate( Cu.cloneInto(instance, sandbox), clonedSchema, draft, sandbox.dereference(clonedSchema), shortCircuit ); } function detectSchemaDraft(schema, defaultDraft = "2019-09") { const { $schema } = schema; if (typeof $schema === "undefined") { return defaultDraft; } switch ($schema) { case "http://json-schema.org/draft-04/schema#": return "4"; case "http://json-schema.org/draft-06/schema#": return "6"; case "http://json-schema.org/draft-07/schema#": return "7"; case "https://json-schema.org/draft/2019-09/schema": return "2019-09"; case "https://json-schema.org/draft/2020-12/schema": return "2020-12"; default: console.error( `Unexpected $schema "${$schema}", defaulting to ${defaultDraft}.` ); return defaultDraft; } } export const JsonSchema = { Validator, validate, detectSchemaDraft, };