json: restrict external refs to https, remove allowFetch options

This commit is contained in:
ochafik 2024-06-28 21:39:02 +01:00
parent 9ba101313e
commit b6abfdb5fe
4 changed files with 25 additions and 18 deletions

View file

@ -838,10 +838,13 @@ public:
auto it = _external_refs.find(url);
if (it != _external_refs.end()) {
target = it->second;
} else {
} else if (url.rfind("https://", 0) == 0) {
// Fetch the referenced schema and resolve its refs
target = _fetch_json(url);
_external_refs[url] = target;
} else {
_errors.push_back("Error resolving ref " + ref + ": unsupported url scheme");
return {json(), "", false};
}
}
if (parts.size() == 1) {

View file

@ -235,9 +235,8 @@ ESCAPED_IN_REGEXPS_BUT_NOT_IN_LITERALS = set('^$.[]()|{}*+?')
class SchemaConverter:
def __init__(self, *, prop_order, allow_fetch, dotall, raw_pattern):
def __init__(self, *, prop_order, dotall, raw_pattern):
self._prop_order = prop_order
self._allow_fetch = allow_fetch
self._dotall = dotall
self._raw_pattern = raw_pattern
self._rules = {
@ -507,7 +506,7 @@ class SchemaConverter:
def _resolve_ref(self, ref: str):
parts = ref.split('#')
assert len(parts) == 2, f'Unsupported ref: {ref}'
assert len(parts) <= 2, f'Unsupported ref: {ref}'
url = parts[0]
target = None
is_local = not url
@ -518,13 +517,18 @@ class SchemaConverter:
target = self._external_refs.get(url)
if target is None:
# Fetch the referenced schema and resolve its refs
target = self._fetch_json(url)
assert url.startswith("https://"), f"Error resolving ref {ref}: unsupported url scheme"
import requests
target = requests.get(url).json()
self._external_refs[url] = target
tokens = parts[1].split('/')
for sel in tokens[1:]:
assert target is not None and sel in target, f'Error resolving ref {ref}: {sel} not in {target}'
target = target[sel]
if len(parts) == 1:
return self.ResolvedRef(target, '', is_local)
else:
tokens = parts[1].split('/')
for sel in tokens[1:]:
assert target is not None and sel in target, f'Error resolving ref {ref}: {sel} not in {target}'
target = target[sel]
return self.ResolvedRef(target, tokens[-1] if tokens else '', is_local)
@ -760,11 +764,6 @@ def main(args_in = None):
given precedence over optional properties.
'''
)
parser.add_argument(
'--allow-fetch',
action='store_true',
default=False,
help='Whether to allow fetching referenced schemas over HTTPS')
parser.add_argument(
'--dotall',
action='store_true',
@ -792,7 +791,6 @@ def main(args_in = None):
schema = json.load(f)
converter = SchemaConverter(
prop_order={name: idx for idx, name in enumerate(args.prop_order)},
allow_fetch=args.allow_fetch,
dotall=args.dotall,
raw_pattern=args.raw_pattern)
converter.visit(schema, '')

View file

@ -264,7 +264,6 @@ const ESCAPED_IN_REGEXPS_BUT_NOT_IN_LITERALS = new Set('^$.[]()|{}*+?');
export class SchemaConverter {
constructor(options) {
this._propOrder = options.prop_order || {};
this._allowFetch = options.allow_fetch || false;
this._dotall = options.dotall || false;
this._rules = {'space': SPACE_RULE};
this._refs = {};
@ -558,6 +557,9 @@ export class SchemaConverter {
target = this._externalRefs.get(url);
if (target === undefined) {
// Fetch the referenced schema and resolve its refs
if (!url.startsWith('https://')) {
throw new Error(`Error resolving ref ${ref}: unsupported url scheme`);
}
target = this._fetchJson(url);
this._externalRefs.set(url, target);
}

View file

@ -2,8 +2,12 @@ import { readFileSync } from "fs"
import { SchemaConverter } from "../examples/server/public/json-schema-to-grammar.mjs"
const [, , file] = process.argv
const url = `file://${file}`
const schema = JSON.parse(readFileSync(file, "utf8"));
let schema;
if (file.startsWith('https://')) {
schema = await (await fetch(file)).json()
} else {
schema = JSON.parse(readFileSync(file, "utf8"));
}
const converter = new SchemaConverter({})
converter.visit(schema, '')
console.log(converter.formatGrammar())