Skip to content

Commit

Permalink
Merge branch 'master' into dependabot/npm_and_yarn/mocha-8.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
epoberezkin committed Jul 4, 2020
2 parents 854dbef + d6aabb8 commit c6a6daa
Show file tree
Hide file tree
Showing 20 changed files with 99 additions and 100 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Expand Up @@ -3,7 +3,6 @@ before_script:
- git submodule update --init
- npm install -g codeclimate-test-reporter
node_js:
- 8
- 10
- 12
- 14
Expand Down
17 changes: 16 additions & 1 deletion README.md
Expand Up @@ -82,6 +82,7 @@ ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json'));
- [Getting started](#getting-started)
- [Frequently Asked Questions](https://github.com/ajv-validator/ajv/blob/master/FAQ.md)
- [Using in browser](#using-in-browser)
- [Ajv and Content Security Policies (CSP)](#ajv-and-content-security-policies-csp)
- [Command line interface](#command-line-interface)
- Validation
- [Keywords](#validation-keywords)
Expand Down Expand Up @@ -238,6 +239,16 @@ Ajv is tested with these browsers:
__Please note__: some frameworks, e.g. Dojo, may redefine global require in such way that is not compatible with CommonJS module format. In such case Ajv bundle has to be loaded before the framework and then you can use global Ajv (see issue [#234](https://github.com/ajv-validator/ajv/issues/234)).


### Ajv and Content Security Policies (CSP)

If you're using Ajv to compile a schema (the typical use) in a browser document that is loaded with a Content Security Policy (CSP), that policy will require a `script-src` directive that includes the value `'unsafe-eval'`.
:warning: NOTE, however, that `unsafe-eval` is NOT recommended in a secure CSP[[1]](https://developer.chrome.com/extensions/contentSecurityPolicy#relaxing-eval), as it has the potential to open the document to cross-site scripting (XSS) attacks.

In order to make use of Ajv without easing your CSP, you can [pre-compile a schema using the CLI](https://github.com/ajv-validator/ajv-cli#compile-schemas). This will transpile the schema JSON into a JavaScript file that exports a `validate` function that works simlarly to a schema compiled at runtime.

Note that pre-compilation of schemas is performed using [ajv-pack](https://github.com/ajv-validator/ajv-pack) and there are [some limitations to the schema features it can compile](https://github.com/ajv-validator/ajv-pack#limitations). A successfully pre-compiled schema is equivalent to the same schema compiled at runtime.


## Command line interface

CLI is available as a separate npm package [ajv-cli](https://github.com/ajv-validator/ajv-cli). It supports:
Expand Down Expand Up @@ -321,7 +332,7 @@ You can add additional formats and replace any of the formats above using [addFo

The option `unknownFormats` allows changing the default behaviour when an unknown format is encountered. In this case Ajv can either fail schema compilation (default) or ignore it (default in versions before 5.0.0). You also can whitelist specific format(s) to be ignored. See [Options](#options) for details.

You can find regular expressions used for format validation and the sources that were used in [formats.js](https://github.com/ajv-validatorv/ajv/blob/master/lib/compile/formats.js).
You can find regular expressions used for format validation and the sources that were used in [formats.js](https://github.com/ajv-validator/ajv/blob/master/lib/compile/formats.js).


## <a name="ref"></a>Combining schemas with $ref
Expand Down Expand Up @@ -722,6 +733,10 @@ isSchemaSecure(schema2); // true
__Please note__: following all these recommendation is not a guarantee that validation of untrusted data is safe - it can still lead to some undesirable results.


##### Content Security Policies (CSP)
See [Ajv and Content Security Policies (CSP)](#ajv-and-content-security-policies-csp)


## ReDoS attack

Certain regular expressions can lead to the exponential evaluation time even with relatively short strings.
Expand Down
40 changes: 1 addition & 39 deletions lib/compile/util.js
Expand Up @@ -13,8 +13,6 @@ module.exports = {
ucs2length: require('./ucs2length'),
varOccurences: varOccurences,
varReplace: varReplace,
cleanUpCode: cleanUpCode,
finalCleanUpCode: finalCleanUpCode,
schemaHasRules: schemaHasRules,
schemaHasRulesExcept: schemaHasRulesExcept,
schemaUnknownRules: schemaUnknownRules,
Expand Down Expand Up @@ -139,42 +137,6 @@ function varReplace(str, dataVar, expr) {
}


var EMPTY_ELSE = /else\s*{\s*}/g
, EMPTY_IF_NO_ELSE = /if\s*\([^)]+\)\s*\{\s*\}(?!\s*else)/g
, EMPTY_IF_WITH_ELSE = /if\s*\(([^)]+)\)\s*\{\s*\}\s*else(?!\s*if)/g;
function cleanUpCode(out) {
return out.replace(EMPTY_ELSE, '')
.replace(EMPTY_IF_NO_ELSE, '')
.replace(EMPTY_IF_WITH_ELSE, 'if (!($1))');
}


var ERRORS_REGEXP = /[^v.]errors/g
, REMOVE_ERRORS = /var errors = 0;|var vErrors = null;|validate.errors = vErrors;/g
, REMOVE_ERRORS_ASYNC = /var errors = 0;|var vErrors = null;/g
, RETURN_VALID = 'return errors === 0;'
, RETURN_TRUE = 'validate.errors = null; return true;'
, RETURN_ASYNC = /if \(errors === 0\) return data;\s*else throw new ValidationError\(vErrors\);/
, RETURN_DATA_ASYNC = 'return data;'
, ROOTDATA_REGEXP = /[^A-Za-z_$]rootData[^A-Za-z0-9_$]/g
, REMOVE_ROOTDATA = /if \(rootData === undefined\) rootData = data;/;

function finalCleanUpCode(out, async) {
var matches = out.match(ERRORS_REGEXP);
if (matches && matches.length == 2) {
out = async
? out.replace(REMOVE_ERRORS_ASYNC, '')
.replace(RETURN_ASYNC, RETURN_DATA_ASYNC)
: out.replace(REMOVE_ERRORS, '')
.replace(RETURN_VALID, RETURN_TRUE);
}

matches = out.match(ROOTDATA_REGEXP);
if (!matches || matches.length !== 3) return out;
return out.replace(REMOVE_ROOTDATA, '');
}


function schemaHasRules(schema, rules) {
if (typeof schema == 'boolean') return !schema;
for (var key in schema) if (rules[key]) return true;
Expand Down Expand Up @@ -253,7 +215,7 @@ function getData($data, lvl, paths) {

function joinPaths (a, b) {
if (a == '""') return b;
return (a + ' + ' + b).replace(/' \+ '/g, '');
return (a + ' + ' + b).replace(/([^\\])' \+ '/g, '$1');
}


Expand Down
9 changes: 9 additions & 0 deletions lib/dot/_limit.jst
Expand Up @@ -17,6 +17,15 @@
, $op = $isMax ? '<' : '>'
, $notOp = $isMax ? '>' : '<'
, $errorKeyword = undefined;

if (!($isData || typeof $schema == 'number' || $schema === undefined)) {
throw new Error($keyword + ' must be number');
}
if (!($isDataExcl || $schemaExcl === undefined
|| typeof $schemaExcl == 'number'
|| typeof $schemaExcl == 'boolean')) {
throw new Error($exclusiveKeyword + ' must be number or boolean');
}
}}

{{? $isDataExcl }}
Expand Down
2 changes: 2 additions & 0 deletions lib/dot/_limitItems.jst
Expand Up @@ -3,6 +3,8 @@
{{# def.setupKeyword }}
{{# def.$data }}

{{# def.numberKeyword }}

{{ var $op = $keyword == 'maxItems' ? '>' : '<'; }}
if ({{# def.$dataNotType:'number' }} {{=$data}}.length {{=$op}} {{=$schemaValue}}) {
{{ var $errorKeyword = $keyword; }}
Expand Down
2 changes: 2 additions & 0 deletions lib/dot/_limitLength.jst
Expand Up @@ -3,6 +3,8 @@
{{# def.setupKeyword }}
{{# def.$data }}

{{# def.numberKeyword }}

{{ var $op = $keyword == 'maxLength' ? '>' : '<'; }}
if ({{# def.$dataNotType:'number' }} {{# def.strLength }} {{=$op}} {{=$schemaValue}}) {
{{ var $errorKeyword = $keyword; }}
Expand Down
2 changes: 2 additions & 0 deletions lib/dot/_limitProperties.jst
Expand Up @@ -3,6 +3,8 @@
{{# def.setupKeyword }}
{{# def.$data }}

{{# def.numberKeyword }}

{{ var $op = $keyword == 'maxProperties' ? '>' : '<'; }}
if ({{# def.$dataNotType:'number' }} Object.keys({{=$data}}).length {{=$op}} {{=$schemaValue}}) {
{{ var $errorKeyword = $keyword; }}
Expand Down
2 changes: 0 additions & 2 deletions lib/dot/allOf.jst
Expand Up @@ -30,5 +30,3 @@
{{= $closingBraces.slice(0,-1) }}
{{?}}
{{?}}

{{# def.cleanUp }}
2 changes: 0 additions & 2 deletions lib/dot/anyOf.jst
Expand Up @@ -39,8 +39,6 @@
} else {
{{# def.resetErrors }}
{{? it.opts.allErrors }} } {{?}}

{{# def.cleanUp }}
{{??}}
{{? $breakOnError }}
if (true) {
Expand Down
2 changes: 0 additions & 2 deletions lib/dot/contains.jst
Expand Up @@ -53,5 +53,3 @@ var {{=$valid}};
{{# def.resetErrors }}
{{?}}
{{? it.opts.allErrors }} } {{?}}

{{# def.cleanUp }}
13 changes: 7 additions & 6 deletions lib/dot/definitions.def
Expand Up @@ -112,12 +112,6 @@
#}}


{{## def.cleanUp: {{ out = it.util.cleanUpCode(out); }} #}}


{{## def.finalCleanUp: {{ out = it.util.finalCleanUpCode(out, $async); }} #}}


{{## def.$data:
{{
var $isData = it.opts.$data && $schema && $schema.$data
Expand All @@ -144,6 +138,13 @@
#}}


{{## def.numberKeyword:
{{? !($isData || typeof $schema == 'number') }}
{{ throw new Error($keyword + ' must be number'); }}
{{?}}
#}}


{{## def.beginDefOut:
{{
var $$outStack = $$outStack || [];
Expand Down
3 changes: 1 addition & 2 deletions lib/dot/dependencies.jst
Expand Up @@ -19,6 +19,7 @@
, $ownProperties = it.opts.ownProperties;

for ($property in $schema) {
if ($property == '__proto__') continue;
var $sch = $schema[$property];
var $deps = Array.isArray($sch) ? $propertyDeps : $schemaDeps;
$deps[$property] = $sch;
Expand Down Expand Up @@ -76,5 +77,3 @@ var missing{{=$lvl}};
{{= $closingBraces }}
if ({{=$errs}} == errors) {
{{?}}

{{# def.cleanUp }}
2 changes: 0 additions & 2 deletions lib/dot/if.jst
Expand Up @@ -65,8 +65,6 @@
{{# def.extraError:'if' }}
}
{{? $breakOnError }} else { {{?}}

{{# def.cleanUp }}
{{??}}
{{? $breakOnError }}
if (true) {
Expand Down
2 changes: 0 additions & 2 deletions lib/dot/items.jst
Expand Up @@ -96,5 +96,3 @@ var {{=$valid}};
{{= $closingBraces }}
if ({{=$errs}} == errors) {
{{?}}

{{# def.cleanUp }}
2 changes: 2 additions & 0 deletions lib/dot/multipleOf.jst
Expand Up @@ -3,6 +3,8 @@
{{# def.setupKeyword }}
{{# def.$data }}

{{# def.numberKeyword }}

var division{{=$lvl}};
if ({{?$isData}}
{{=$schemaValue}} !== undefined && (
Expand Down
11 changes: 6 additions & 5 deletions lib/dot/properties.jst
Expand Up @@ -28,9 +28,9 @@
, $nextData = 'data' + $dataNxt
, $dataProperties = 'dataProperties' + $lvl;

var $schemaKeys = Object.keys($schema || {})
var $schemaKeys = Object.keys($schema || {}).filter(notProto)
, $pProperties = it.schema.patternProperties || {}
, $pPropertyKeys = Object.keys($pProperties)
, $pPropertyKeys = Object.keys($pProperties).filter(notProto)
, $aProperties = it.schema.additionalProperties
, $someProperties = $schemaKeys.length || $pPropertyKeys.length
, $noAdditional = $aProperties === false
Expand All @@ -42,8 +42,11 @@
, $currentBaseId = it.baseId;

var $required = it.schema.required;
if ($required && !(it.opts.$data && $required.$data) && $required.length < it.opts.loopRequired)
if ($required && !(it.opts.$data && $required.$data) && $required.length < it.opts.loopRequired) {
var $requiredHash = it.util.toHash($required);
}

function notProto(p) { return p !== '__proto__'; }
}}


Expand Down Expand Up @@ -240,5 +243,3 @@ var {{=$nextValid}} = true;
{{= $closingBraces }}
if ({{=$errs}} == errors) {
{{?}}

{{# def.cleanUp }}
2 changes: 0 additions & 2 deletions lib/dot/propertyNames.jst
Expand Up @@ -50,5 +50,3 @@ var {{=$errs}} = errors;
{{= $closingBraces }}
if ({{=$errs}} == errors) {
{{?}}

{{# def.cleanUp }}
6 changes: 0 additions & 6 deletions lib/dot/validate.jst
Expand Up @@ -254,12 +254,6 @@
var {{=$valid}} = errors === errs_{{=$lvl}};
{{?}}

{{# def.cleanUp }}

{{? $top }}
{{# def.finalCleanUp }}
{{?}}

{{
function $shouldUseGroup($rulesGroup) {
var rules = $rulesGroup.rules;
Expand Down
51 changes: 51 additions & 0 deletions spec/ajv.spec.js
Expand Up @@ -512,5 +512,56 @@ describe('Ajv', function () {
});
});
});

describe('sub-schema validation outside of definitions during compilation', function() {
it('maximum', function() {
passValidationThrowCompile({
$ref: '#/foo',
foo: {maximum: 'bar'}
});
});

it('exclusiveMaximum', function() {
passValidationThrowCompile({
$ref: '#/foo',
foo: {exclusiveMaximum: 'bar'}
});
});

it('maxItems', function() {
passValidationThrowCompile({
$ref: '#/foo',
foo: {maxItems: 'bar'}
});
});

it('maxLength', function() {
passValidationThrowCompile({
$ref: '#/foo',
foo: {maxLength: 'bar'}
});
});

it('maxProperties', function() {
passValidationThrowCompile({
$ref: '#/foo',
foo: {maxProperties: 'bar'}
});
});

it('multipleOf', function() {
passValidationThrowCompile({
$ref: '#/foo',
foo: {maxProperties: 'bar'}
});
});

function passValidationThrowCompile(schema) {
ajv.validateSchema(schema) .should.equal(true);
should.throw(function() {
ajv.compile(schema);
});
}
});
});
});
28 changes: 0 additions & 28 deletions spec/issues/388_code_clean-up.spec.js

This file was deleted.

0 comments on commit c6a6daa

Please sign in to comment.