AJV relies on eval and limits usage of jsonforms

We recently discovered that AJV relies on eval which prevented us from using our framework due to its reliance on JsonForms but only on certain environments where eval is restricted due to CSP policy. This was like a landmine that exploded in our face after a year of investment into a json forms based framework.

This was a blocker for one org to use our framework, and we fought hard to allow eval in their CSP but we were forced to deliver a solution that was less than ideal which involved creating a custom AJV-like function which matches the signatures of AJV in its limited use by json forms but does not use eval.

Prior to creating our own AJV that doesn’t rely on eval, I did a search for solutions. The only thing I found was a possible precompiled validator function using ajv which, from what I understood, required a schema that was statically defined at build time. This of course would not work for us because the schemas are almost never static and come from APIs which are generated from customer-defined data models.

I’m writing here for a few purposes:

  1. Check for any better solutions which don’t require schemas to be static

  2. Check if someone can explain the reason why AJV uses eval? This might not be the best place to ask, but it seemed like a bad decision to use eval here, since json schemas don’t contain JavaScript snippets that need to evaluated, and now we just need to trust that ajv doesn’t accidentally evaluate XSS in user controllable schemas.

  3. Check how well-known is this limitation of jsonforms, if it’s not prominently displayed anywhere that jsonforms depends on AJV which requires a certain CSP policy to run - that should probably be added in my opinion.

  4. Check on interest for our custom ajv that can be substituted at least for limited usages of draft 7. I feel like if jsonforms came with this as an option it might help others who need to run json forms on a restricted CSP.

1 Like

Hi @i832513,

I’m sorry to hear that.

When using AJV within the frontend there is no alternative to eval at runtime or precompiling schemas beforehand.

Depending on your requirements and architecture you could think about running the validation somewhere else. JSON Forms supports integrating errors from external sources. You could run AJV on the backend, or if your rules allow it, in a web worker.

AJV analyses the JSON Schema and performs code generation. This code is then evaled to produce the validation function. From then on, only this validation function is used throughout the lifetime of the respective JSON Schema. This architecture allows AJV to (re-)validate extremely fast. In our performance benchmarks AJV was never the culprit for actual slow downs in production.

This is at the core of their architecture, so they will not change that going forward.

We have an open ticket for this, see #1498. We don’t have a warning on the website, but it makes sense to me to add one.

In general we are interested in abstracting the validation support in JSON Forms away from AJV. Not only for this use case, but in general. For example the default AJV is always bundled, even if the user hands in a different AJV instance. Other users don’t use AJV at all but solely rely on backend validation and they also have the penalty of bundling AJV.

Once this is abstracted, any other validator could be included, without them “masking as AJV” which is the only current workaround.

We would accept a contribution in this direction and then release a new major version of JSON Forms as we’ll likely break compatibility then. If you intend to contribute this feature, I would like to suggest to first create a draft-pr outlining the intended changes so we can give feedback before a full implementation is done.

For your use case it would be best to include a validator which is not using code-gen but does it on the fly based on the JSON Schema. If you found one or wrote your own, you should be able to already integrate it by masking it as an AJV object.

For JSON Forms itself, it seems a bit out of scope to offer such a validator itself. However if it’s really minimal with the declared scope to not support all of JSON schema but a very limited subset, then we could think about it.

1 Like

Thanks @sdirix for the quick reply. Your answer for AJV using eval is sufficient for the reality of the situation but it’s disappointing to hear. To summarize, they use eval because someone made a bad choice early on by making it part of their core architecture and it’s too late now to change that without rewriting the whole thing. I was leaving some room for a possibility where a particular subset of json schema functionality would be difficult to do without code generation but that doesn’t seem likely.

Code generation is one way to approach the problem, but on the surface it feels completely unnecessary. Instead of generating code then executing it… just write the code that executes in one step. I suppose performance is one concern but as you say it was not a major contributor of any issue there. My concern was more around the fact that using eval is inherently dangerous when the content of that eval is partially derived by user-specified content (the json schema in this case). To use it then requires trust that the library doesn’t have any eval vulnerabilities that would inject XSS, not to mention the fact that you can’t use the framework at all when CSP disables eval.

I’ll talk with my org about the possibility of contributing our “internalAJV” which we lazy load any time we detect that eval is not allowed. We built the validator to have the same interface, in terms of producing error objects with the same exact error duck type. This might take work to contribute so we might not do it if there is not a big interest in it.

1 Like

Hi @i832513,

That’s a bit harsh. AJV wins any performance comparison by a long mile, so the approach has its benefits. Which makes sense, they analyze the JSON Schema only one time, with that they are able to generate code like if (obj.foo.length > 5) then produceLengthError()). Any “normal” validator which takes in a schema and/or maintains some lookup-map will be slower.

AJV is extremely popular (>100 million downloads a week). If this library can’t be trusted, then basically no library “is safe”.

There are alternative libraries out there, e.g. jsonschema. Did you try them?

@sdirix ,

Thanks, I realize now how code generation could produce a performance optimized result, especially if the validation is performed many times. Earlier I was thinking that a non code generated validator would perform roughly the same number of operations, but thinking again I see how that’s wrong.

For example, checking if a schema has a “oneOf” would not be required in generated code nor would you need to check for anything at all just validate what needs to validate. Just trying to think of a solution to improve my validator so that it doesn’t have to check for oneOf for every level in the schema made me realize how easy it would be to avoid those wasted cycles with generated code. I can think of some better approaches that avoid these checks but it could not compete with the generated code at large scales.

I think for the moment our algorithm is not optimized though it might be “good enough”. I’ll give it some more thought though. Thanks for this additional insight into the reasoning.

1 Like