@contrast/assess 1.46.1 → 1.46.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +14 -11
- package/lib/crypto-analysis/install/crypto.test.js +0 -146
- package/lib/crypto-analysis/install/math.test.js +0 -65
- package/lib/dataflow/index.test.js +0 -36
- package/lib/dataflow/propagation/index.test.js +0 -103
- package/lib/dataflow/propagation/install/JSON/index.test.js +0 -50
- package/lib/dataflow/propagation/install/JSON/parse-fn.test.js +0 -232
- package/lib/dataflow/propagation/install/JSON/parse.test.js +0 -968
- package/lib/dataflow/propagation/install/JSON/stringify.test.js +0 -265
- package/lib/dataflow/propagation/install/array-prototype-join.test.js +0 -106
- package/lib/dataflow/propagation/install/buffer.test.js +0 -112
- package/lib/dataflow/propagation/install/contrast-methods/add.test.js +0 -94
- package/lib/dataflow/propagation/install/contrast-methods/index.test.js +0 -49
- package/lib/dataflow/propagation/install/contrast-methods/number.test.js +0 -50
- package/lib/dataflow/propagation/install/contrast-methods/string.test.js +0 -148
- package/lib/dataflow/propagation/install/contrast-methods/tag.test.js +0 -145
- package/lib/dataflow/propagation/install/decode-uri-component.test.js +0 -78
- package/lib/dataflow/propagation/install/ejs/escape-xml.test.js +0 -69
- package/lib/dataflow/propagation/install/ejs/template.test.js +0 -62
- package/lib/dataflow/propagation/install/encode-uri.test.js +0 -83
- package/lib/dataflow/propagation/install/escape-html.test.js +0 -71
- package/lib/dataflow/propagation/install/escape.test.js +0 -73
- package/lib/dataflow/propagation/install/fastify-send.test.js +0 -42
- package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.test.js +0 -71
- package/lib/dataflow/propagation/install/isnumeric-0.test.js +0 -58
- package/lib/dataflow/propagation/install/joi/any.test.js +0 -270
- package/lib/dataflow/propagation/install/joi/array.test.js +0 -912
- package/lib/dataflow/propagation/install/joi/boolean.test.js +0 -103
- package/lib/dataflow/propagation/install/joi/expression.test.js +0 -76
- package/lib/dataflow/propagation/install/joi/index.test.js +0 -39
- package/lib/dataflow/propagation/install/joi/number.test.js +0 -103
- package/lib/dataflow/propagation/install/joi/object.test.js +0 -119
- package/lib/dataflow/propagation/install/joi/ref.test.js +0 -607
- package/lib/dataflow/propagation/install/joi/string-schema.test.js +0 -513
- package/lib/dataflow/propagation/install/mongoose/index.test.js +0 -42
- package/lib/dataflow/propagation/install/mongoose/schema-map.test.js +0 -348
- package/lib/dataflow/propagation/install/mongoose/schema-mixed.test.js +0 -511
- package/lib/dataflow/propagation/install/mongoose/schema-string.test.js +0 -199
- package/lib/dataflow/propagation/install/mustache-escape.test.js +0 -62
- package/lib/dataflow/propagation/install/mysql-connection-escape.test.js +0 -74
- package/lib/dataflow/propagation/install/parse-int.test.js +0 -48
- package/lib/dataflow/propagation/install/path/basename.test.js +0 -143
- package/lib/dataflow/propagation/install/path/dirname.test.js +0 -167
- package/lib/dataflow/propagation/install/path/extname.test.js +0 -141
- package/lib/dataflow/propagation/install/path/format.test.js +0 -250
- package/lib/dataflow/propagation/install/path/index.test.js +0 -45
- package/lib/dataflow/propagation/install/path/join-and-resolve.test.js +0 -485
- package/lib/dataflow/propagation/install/path/normalize.test.js +0 -176
- package/lib/dataflow/propagation/install/path/parse.test.js +0 -238
- package/lib/dataflow/propagation/install/path/relative.test.js +0 -239
- package/lib/dataflow/propagation/install/path/toNamespacedPath.test.js +0 -158
- package/lib/dataflow/propagation/install/pug/index.test.js +0 -55
- package/lib/dataflow/propagation/install/pug-runtime-escape.test.js +0 -69
- package/lib/dataflow/propagation/install/querystring/escape.test.js +0 -63
- package/lib/dataflow/propagation/install/querystring/index.test.js +0 -40
- package/lib/dataflow/propagation/install/querystring/parse.test.js +0 -272
- package/lib/dataflow/propagation/install/querystring/stringify.test.js +0 -301
- package/lib/dataflow/propagation/install/reg-exp-prototype-exec.test.js +0 -283
- package/lib/dataflow/propagation/install/send.test.js +0 -63
- package/lib/dataflow/propagation/install/sequelize/query-generator.test.js +0 -74
- package/lib/dataflow/propagation/install/sequelize/sql-string.test.js +0 -119
- package/lib/dataflow/propagation/install/sql-template-strings.test.js +0 -100
- package/lib/dataflow/propagation/install/string/concat.test.js +0 -145
- package/lib/dataflow/propagation/install/string/format-methods.test.js +0 -74
- package/lib/dataflow/propagation/install/string/html-methods.test.js +0 -177
- package/lib/dataflow/propagation/install/string/index.test.js +0 -103
- package/lib/dataflow/propagation/install/string/match-all.test.js +0 -412
- package/lib/dataflow/propagation/install/string/match.test.js +0 -374
- package/lib/dataflow/propagation/install/string/replace.test.js +0 -601
- package/lib/dataflow/propagation/install/string/slice.test.js +0 -278
- package/lib/dataflow/propagation/install/string/split.test.js +0 -513
- package/lib/dataflow/propagation/install/string/substring.test.js +0 -251
- package/lib/dataflow/propagation/install/string/trim.test.js +0 -135
- package/lib/dataflow/propagation/install/unescape.test.js +0 -78
- package/lib/dataflow/propagation/install/url/domain-parsers.test.js +0 -63
- package/lib/dataflow/propagation/install/url/parse.test.js +0 -391
- package/lib/dataflow/propagation/install/url/searchParams.test.js +0 -538
- package/lib/dataflow/propagation/install/url/url.test.js +0 -466
- package/lib/dataflow/propagation/install/util-format.test.js +0 -336
- package/lib/dataflow/propagation/install/validator/hooks.test.js +0 -211
- package/lib/dataflow/sinks/index.test.js +0 -78
- package/lib/dataflow/sinks/install/child-process.test.js +0 -338
- package/lib/dataflow/sinks/install/eval.test.js +0 -95
- package/lib/dataflow/sinks/install/express/index.test.js +0 -33
- package/lib/dataflow/sinks/install/express/reflected-xss.test.js +0 -109
- package/lib/dataflow/sinks/install/express/unvalidated-redirect.test.js +0 -144
- package/lib/dataflow/sinks/install/fastify/index.test.js +0 -32
- package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.test.js +0 -130
- package/lib/dataflow/sinks/install/fs.test.js +0 -138
- package/lib/dataflow/sinks/install/function.test.js +0 -103
- package/lib/dataflow/sinks/install/hapi/index.test.js +0 -32
- package/lib/dataflow/sinks/install/hapi/unvalidated-redirect.test.js +0 -130
- package/lib/dataflow/sinks/install/http/index.test.js +0 -33
- package/lib/dataflow/sinks/install/http/request.test.js +0 -184
- package/lib/dataflow/sinks/install/http/server-response.test.js +0 -160
- package/lib/dataflow/sinks/install/koa/index.test.js +0 -32
- package/lib/dataflow/sinks/install/koa/unvalidated-redirect.test.js +0 -200
- package/lib/dataflow/sinks/install/libxmljs.test.js +0 -158
- package/lib/dataflow/sinks/install/marsdb.test.js +0 -166
- package/lib/dataflow/sinks/install/mongodb.test.js +0 -617
- package/lib/dataflow/sinks/install/mssql.test.js +0 -134
- package/lib/dataflow/sinks/install/mysql.test.js +0 -224
- package/lib/dataflow/sinks/install/node-serialize.test.js +0 -83
- package/lib/dataflow/sinks/install/postgres.test.js +0 -152
- package/lib/dataflow/sinks/install/restify.test.js +0 -140
- package/lib/dataflow/sinks/install/sequelize.test.js +0 -100
- package/lib/dataflow/sinks/install/sqlite3.test.js +0 -118
- package/lib/dataflow/sinks/install/vm.test.js +0 -326
- package/lib/dataflow/sources/handler.test.js +0 -501
- package/lib/dataflow/sources/index.test.js +0 -60
- package/lib/dataflow/sources/install/body-parser1.test.js +0 -244
- package/lib/dataflow/sources/install/busboy.test.js +0 -152
- package/lib/dataflow/sources/install/cookie-parser1.test.js +0 -141
- package/lib/dataflow/sources/install/express/params.test.js +0 -110
- package/lib/dataflow/sources/install/express/parsedUrl.test.js +0 -107
- package/lib/dataflow/sources/install/fastify/fastify.test.js +0 -207
- package/lib/dataflow/sources/install/fastify/index.test.js +0 -33
- package/lib/dataflow/sources/install/formidable1.test.js +0 -119
- package/lib/dataflow/sources/install/graphql-http.test.js +0 -133
- package/lib/dataflow/sources/install/hapi/hapi.test.js +0 -171
- package/lib/dataflow/sources/install/hapi/index.test.js +0 -33
- package/lib/dataflow/sources/install/http.test.js +0 -149
- package/lib/dataflow/sources/install/koa/index.test.js +0 -40
- package/lib/dataflow/sources/install/koa/koa-bodyparsers.test.js +0 -160
- package/lib/dataflow/sources/install/koa/koa-multer.test.js +0 -197
- package/lib/dataflow/sources/install/koa/koa-routers.test.js +0 -146
- package/lib/dataflow/sources/install/koa/koa2.test.js +0 -145
- package/lib/dataflow/sources/install/multer1.test.js +0 -143
- package/lib/dataflow/sources/install/qs6.test.js +0 -131
- package/lib/dataflow/sources/install/querystring.test.js +0 -82
- package/lib/dataflow/sources/install/restify/fieldedTextBodyParser.test.js +0 -86
- package/lib/dataflow/sources/install/restify/index.test.js +0 -38
- package/lib/dataflow/sources/install/restify/jsonBodyParser.test.js +0 -140
- package/lib/dataflow/sources/install/restify/router.test.js +0 -81
- package/lib/dataflow/tag-utils-complete.test.js +0 -27
- package/lib/dataflow/tag-utils.test.js +0 -192
- package/lib/dataflow/tracker.test.js +0 -216
- package/lib/dataflow/utils/is-safe-content-type.test.js +0 -16
- package/lib/dataflow/utils/is-vulnerable.test.js +0 -115
- package/lib/event-factory.test.js +0 -326
- package/lib/get-policy.test.js +0 -194
- package/lib/get-source-context.test.js +0 -161
- package/lib/index.test.js +0 -45
- package/lib/make-source-context.test.js +0 -50
- package/lib/response-scanning/handlers/index.test.js +0 -419
- package/lib/response-scanning/handlers/utils.test.js +0 -380
- package/lib/response-scanning/index.test.js +0 -41
- package/lib/response-scanning/install/http.test.js +0 -175
- package/lib/rule-scopes.test.js +0 -27
- package/lib/sampler/common.test.js +0 -101
- package/lib/sampler/index.test.js +0 -313
- package/lib/session-configuration/handlers.test.js +0 -84
- package/lib/session-configuration/index.test.js +0 -36
- package/lib/session-configuration/install/express-session.test.js +0 -218
- package/lib/session-configuration/install/fastify-cookie.test.js +0 -63
- package/lib/session-configuration/install/hapi.test.js +0 -269
- package/lib/session-configuration/install/koa.test.js +0 -92
|
@@ -1,912 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { expect } = require('chai');
|
|
4
|
-
const util = require('util');
|
|
5
|
-
const { initAssessFixture } = require('@contrast/test/fixtures');
|
|
6
|
-
const {
|
|
7
|
-
DataflowTag: {
|
|
8
|
-
UNTRUSTED,
|
|
9
|
-
STRING_TYPE_CHECKED,
|
|
10
|
-
CUSTOM_VALIDATED
|
|
11
|
-
}
|
|
12
|
-
} = require('@contrast/common');
|
|
13
|
-
|
|
14
|
-
function makeTag(string, tag = UNTRUSTED) {
|
|
15
|
-
return { [tag]: [0, string.length - 1] };
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function makeTags(string, ...tags) {
|
|
19
|
-
const ret = {};
|
|
20
|
-
|
|
21
|
-
tags.forEach((t) => {
|
|
22
|
-
ret[t] = [0, string.length - 1];
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
return ret;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function makeSchema(chain, base) {
|
|
29
|
-
for (let i = 0; i < chain.length; i++) {
|
|
30
|
-
const { fn, args = [] } = chain[i];
|
|
31
|
-
base = base[fn](...(typeof args === 'function' ? args() : args));
|
|
32
|
-
}
|
|
33
|
-
return base;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
describe('assess dataflow propagation joi array schema validation', function() {
|
|
37
|
-
let core, joi, tracker, trackString, simulateRequestScope, makeArraySchema;
|
|
38
|
-
|
|
39
|
-
beforeEach(function() {
|
|
40
|
-
({ core, simulateRequestScope, trackString } = initAssessFixture());
|
|
41
|
-
tracker = core.assess.dataflow.tracker;
|
|
42
|
-
core.config.assess.trust_custom_validators = true;
|
|
43
|
-
|
|
44
|
-
core.depHooks.resolve.withArgs({ name: 'joi', file: 'lib/types/string.js', version: '>=17 <18' }).yields(require('joi-17/lib/types/string'));
|
|
45
|
-
core.depHooks.resolve.withArgs({ name: 'joi', file: 'lib/types/keys.js', version: '>=17 <18' }).yields(require('joi-17/lib/types/keys'));
|
|
46
|
-
core.depHooks.resolve.withArgs({ name: 'joi', file: 'lib/types/any', version: '>=17 <18' }).yields(require('joi-17/lib/types/any'));
|
|
47
|
-
core.depHooks.resolve.withArgs({ name: 'joi', file: 'lib/validator', version: '>=17 <18' }).yields(require('joi-17/lib/validator'));
|
|
48
|
-
core.depHooks.resolve.withArgs({ name: 'joi', file: 'lib/values.js', version: '>=17 <18' }).yields(require('joi-17/lib/values'));
|
|
49
|
-
|
|
50
|
-
require('./index')(core).install();
|
|
51
|
-
joi = require('joi-17');
|
|
52
|
-
makeArraySchema = function(chain) {
|
|
53
|
-
return makeSchema([{ fn: 'array' }, ...chain], joi);
|
|
54
|
-
};
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
afterEach(function() {
|
|
58
|
-
Object.keys(require.cache).forEach((key) => {
|
|
59
|
-
if (key.includes('joi')) {
|
|
60
|
-
delete require.cache[key];
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
describe('.items()', function() {
|
|
66
|
-
const xyzzy = 'xyzzy';
|
|
67
|
-
const plover = 22;
|
|
68
|
-
const twisty = 'twisty-passages';
|
|
69
|
-
const colossal = 44;
|
|
70
|
-
const refData = [xyzzy, plover, twisty, colossal];
|
|
71
|
-
|
|
72
|
-
const itemsPositionTests = [
|
|
73
|
-
// as soon as the first element is validated as a string the
|
|
74
|
-
// remaining elements don't get validated in any way, so they
|
|
75
|
-
// won't get tagged. a single string satisfies both .has() and
|
|
76
|
-
// .items(joi.string().required()).
|
|
77
|
-
{
|
|
78
|
-
chain: [
|
|
79
|
-
{ fn: 'items', args: () => [joi.string().required(), joi.any()] },
|
|
80
|
-
{ fn: 'max', args: [4] },
|
|
81
|
-
{ fn: 'min', args: [2] },
|
|
82
|
-
{ fn: 'length', args: [4] },
|
|
83
|
-
{ fn: 'has', args: () => [joi.string()] },
|
|
84
|
-
{ fn: 'single', args: [] }
|
|
85
|
-
],
|
|
86
|
-
data: refData,
|
|
87
|
-
tags: [
|
|
88
|
-
{ ...makeTag(xyzzy), ...makeTag(xyzzy, STRING_TYPE_CHECKED) },
|
|
89
|
-
{ ...makeTag(plover) },
|
|
90
|
-
{ ...makeTag(twisty) },
|
|
91
|
-
{ ...makeTag(colossal) }
|
|
92
|
-
]
|
|
93
|
-
}
|
|
94
|
-
];
|
|
95
|
-
|
|
96
|
-
// build additional tests with .items() in each position of the chain
|
|
97
|
-
const refTest = itemsPositionTests[0];
|
|
98
|
-
for (let i = 1; i < refTest.chain.length; i++) {
|
|
99
|
-
itemsPositionTests[i] = Object.assign({}, refTest);
|
|
100
|
-
itemsPositionTests[i].chain = rotate(refTest.chain, i);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
itemsPositionTests.forEach((t) => {
|
|
104
|
-
const reducer = (acc, i) => {
|
|
105
|
-
acc.push(i.fn);
|
|
106
|
-
return acc;
|
|
107
|
-
};
|
|
108
|
-
const text = t.chain.reduce(reducer, []).join(', ');
|
|
109
|
-
|
|
110
|
-
it(`works in ${text} order`, function() {
|
|
111
|
-
simulateRequestScope(() => {
|
|
112
|
-
const schema = makeArraySchema(t.chain);
|
|
113
|
-
const data = t.data.map((d) => trackString(d));
|
|
114
|
-
const { value, error } = schema.validate(data);
|
|
115
|
-
|
|
116
|
-
// check each element of the array
|
|
117
|
-
for (let i = 0; i < value.length; i++) {
|
|
118
|
-
const v = value[i];
|
|
119
|
-
const td = tracker.getData(v);
|
|
120
|
-
expect(error).equal(undefined, `${t.fn} for ${v}`);
|
|
121
|
-
if (typeof v === 'string') {
|
|
122
|
-
expect(td).to.not.be.null;
|
|
123
|
-
const tags = t.tags[i];
|
|
124
|
-
expect(td.tags).eql(tags, `${t.fn} for ${v}(${i})`);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
const dataPositionTests = [
|
|
132
|
-
// as soon as the first element is validated as a string the
|
|
133
|
-
// remaining elements don't get validated in any way, so they
|
|
134
|
-
// won't get tagged. a single string satisfies both .has() and
|
|
135
|
-
// .items(joi.string().required()).
|
|
136
|
-
{
|
|
137
|
-
name: 'string required',
|
|
138
|
-
chain: [
|
|
139
|
-
{ fn: 'items', args: () => [joi.string().required(), joi.any()] }
|
|
140
|
-
],
|
|
141
|
-
data: undefined,
|
|
142
|
-
tags: undefined
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
name: 'string and number required',
|
|
146
|
-
chain: [
|
|
147
|
-
{
|
|
148
|
-
fn: 'items',
|
|
149
|
-
args: () => [
|
|
150
|
-
joi.string().required(),
|
|
151
|
-
joi.number().required(),
|
|
152
|
-
joi.any()
|
|
153
|
-
]
|
|
154
|
-
}
|
|
155
|
-
],
|
|
156
|
-
data: undefined,
|
|
157
|
-
tags: undefined
|
|
158
|
-
}
|
|
159
|
-
];
|
|
160
|
-
|
|
161
|
-
//
|
|
162
|
-
// for each test generate a test for each permutation of data order
|
|
163
|
-
//
|
|
164
|
-
for (const t of dataPositionTests) {
|
|
165
|
-
const indexPermutations = permutations(Object.keys(refData));
|
|
166
|
-
let data;
|
|
167
|
-
let dataText;
|
|
168
|
-
let tags;
|
|
169
|
-
for (const indexes of indexPermutations) {
|
|
170
|
-
data = [];
|
|
171
|
-
dataText = [];
|
|
172
|
-
tags = [];
|
|
173
|
-
let firstString = true;
|
|
174
|
-
for (let i = 0; i < indexes.length; i++) {
|
|
175
|
-
if (typeof refData[indexes[i]] === 'string') {
|
|
176
|
-
data[i] = () => trackString(refData[indexes[i]]);
|
|
177
|
-
if (firstString) {
|
|
178
|
-
tags[i] = {
|
|
179
|
-
...makeTag(refData[indexes[i]]),
|
|
180
|
-
...makeTag(refData[indexes[i]], STRING_TYPE_CHECKED)
|
|
181
|
-
};
|
|
182
|
-
firstString = false;
|
|
183
|
-
} else {
|
|
184
|
-
tags[i] = { ...makeTag(refData[indexes[i]]) };
|
|
185
|
-
}
|
|
186
|
-
} else {
|
|
187
|
-
data[i] = refData[indexes[i]];
|
|
188
|
-
tags[i] = [];
|
|
189
|
-
}
|
|
190
|
-
dataText[i] = refData[indexes[i]];
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const text = `${t.name} in ${util.format(dataText)}`;
|
|
194
|
-
|
|
195
|
-
it(`${text}`, function() {
|
|
196
|
-
simulateRequestScope(() => {
|
|
197
|
-
data = data.map(i => {
|
|
198
|
-
if (typeof i === 'function') {
|
|
199
|
-
return i();
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return i;
|
|
203
|
-
});
|
|
204
|
-
// run the test we just constructed
|
|
205
|
-
const schema = makeArraySchema(t.chain);
|
|
206
|
-
const { value, error } = schema.validate(data);
|
|
207
|
-
|
|
208
|
-
// check each element of the array
|
|
209
|
-
for (let i = 0; i < value.length; i++) {
|
|
210
|
-
const v = value[i];
|
|
211
|
-
let td = tracker.getData(v);
|
|
212
|
-
expect(error).equal(undefined, `${t.name} for ${v}`);
|
|
213
|
-
if (typeof v === 'string') {
|
|
214
|
-
expect(td).to.not.be.null;
|
|
215
|
-
expect(td.tags).eql(tags[i], `${v}(${i})`);
|
|
216
|
-
// and the tags should be correct in the original array too
|
|
217
|
-
td = tracker.getData(data[i]);
|
|
218
|
-
expect(td).to.not.be.null;
|
|
219
|
-
expect(td.tags).eql(tags[i], `${v}(${i})`);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
describe('.has()', function() {
|
|
229
|
-
it('.has() tags a single string when using .valid()', function() {
|
|
230
|
-
simulateRequestScope(() => {
|
|
231
|
-
// 'b' is valid so tracking should be removed from it
|
|
232
|
-
const schema = joi.string().valid('b');
|
|
233
|
-
const objSchema = joi.object().valid({ prop: 'c' });
|
|
234
|
-
const data = [trackString('a'), trackString('b'), { prop: trackString('c') }];
|
|
235
|
-
|
|
236
|
-
const j = joi.array().has(schema)
|
|
237
|
-
.has(objSchema);
|
|
238
|
-
const { value, error } = j.validate(data);
|
|
239
|
-
|
|
240
|
-
expect(error).undefined;
|
|
241
|
-
|
|
242
|
-
const td = tracker.getData(value[0]);
|
|
243
|
-
expect(td).to.not.be.null;
|
|
244
|
-
expect(td)
|
|
245
|
-
.property('tags')
|
|
246
|
-
.eql({ ...makeTag('b') });
|
|
247
|
-
|
|
248
|
-
expect(tracker.getData(value[1])).to.be.null;
|
|
249
|
-
expect(tracker.getData(value[2].prop)).to.be.null;
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
it('.has() tags a single string when using .string()', function() {
|
|
254
|
-
simulateRequestScope(() => {
|
|
255
|
-
const schema = joi.string();
|
|
256
|
-
const a = trackString('a');
|
|
257
|
-
const b = trackString('b');
|
|
258
|
-
const data = [a, b];
|
|
259
|
-
|
|
260
|
-
const j = joi.array().has(schema);
|
|
261
|
-
const { value, error } = j.validate(data);
|
|
262
|
-
|
|
263
|
-
expect(error).undefined;
|
|
264
|
-
|
|
265
|
-
const tagA = makeTags(a, UNTRUSTED, STRING_TYPE_CHECKED);
|
|
266
|
-
const tagB = makeTags(b, UNTRUSTED);
|
|
267
|
-
|
|
268
|
-
// only the first value that passes is needed by .has()
|
|
269
|
-
const checks = [
|
|
270
|
-
{ name: 'value[0]', value: value[0], tags: tagA },
|
|
271
|
-
{ name: 'value[1]', value: value[1], tags: tagB },
|
|
272
|
-
{ name: 'a', value: a, tags: tagA },
|
|
273
|
-
{ name: 'b', value: b, tags: tagB }
|
|
274
|
-
];
|
|
275
|
-
checks.forEach(({ name, value, tags }) => {
|
|
276
|
-
const td = tracker.getData(value);
|
|
277
|
-
expect(td).to.not.be.null;
|
|
278
|
-
expect(td.tags).eql(tags, `for ${name}`);
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
it('.has() tags a string embedded in an object', function() {
|
|
284
|
-
simulateRequestScope(() => {
|
|
285
|
-
const schema = joi.object({ xyzzy: joi.string() });
|
|
286
|
-
const a = trackString('a');
|
|
287
|
-
const b = trackString('b');
|
|
288
|
-
const data = [{ xyzzy: a }, b];
|
|
289
|
-
|
|
290
|
-
const j = joi.array().has(schema);
|
|
291
|
-
const { value, error } = j.validate(data);
|
|
292
|
-
|
|
293
|
-
const tags1 = makeTags('a', UNTRUSTED, STRING_TYPE_CHECKED);
|
|
294
|
-
const tags2 = makeTags('b', UNTRUSTED);
|
|
295
|
-
|
|
296
|
-
expect(error).undefined;
|
|
297
|
-
|
|
298
|
-
let td = tracker.getData(a);
|
|
299
|
-
expect(td).to.not.be.null;
|
|
300
|
-
expect(td.tags).eql(tags1, 'a should be untrusted');
|
|
301
|
-
td = tracker.getData(value[0].xyzzy);
|
|
302
|
-
expect(td).to.not.be.null;
|
|
303
|
-
expect(td.tags).eql(tags1, 'xyzzy.a should be string-type-checked');
|
|
304
|
-
|
|
305
|
-
td = tracker.getData(b);
|
|
306
|
-
expect(td).to.not.be.null;
|
|
307
|
-
expect(td.tags).eql(tags2, 'b should be untrusted');
|
|
308
|
-
td = tracker.getData(value[1]);
|
|
309
|
-
expect(td).to.not.be.null;
|
|
310
|
-
expect(td.tags).eql(tags2);
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
describe('array length constraints', function() {
|
|
316
|
-
const lengthTests = [
|
|
317
|
-
{ fn: 'length', args: [4] },
|
|
318
|
-
{ fn: 'max', args: [4] },
|
|
319
|
-
{ fn: 'min', args: [2] },
|
|
320
|
-
{ fn: 'single', args: [] }
|
|
321
|
-
];
|
|
322
|
-
|
|
323
|
-
for (const t of lengthTests) {
|
|
324
|
-
const xyzzy = () => trackString('xyzzy');
|
|
325
|
-
const plover = () => trackString('plover');
|
|
326
|
-
const dragon = () => trackString('dragon');
|
|
327
|
-
const twisty = () => trackString('twisty');
|
|
328
|
-
let data = [xyzzy, plover, dragon, twisty];
|
|
329
|
-
|
|
330
|
-
it(`${t.fn} propagates tags`, function() {
|
|
331
|
-
simulateRequestScope(() => {
|
|
332
|
-
data = data.map(i => i());
|
|
333
|
-
const schema = makeArraySchema([t]);
|
|
334
|
-
const { value, error } = schema.validate(data);
|
|
335
|
-
expect(error).undefined;
|
|
336
|
-
|
|
337
|
-
for (let i = 0; i < value.length; i++) {
|
|
338
|
-
const v = value[i];
|
|
339
|
-
const tags = data.map((d) => makeTags(d, UNTRUSTED));
|
|
340
|
-
|
|
341
|
-
let td = tracker.getData(v);
|
|
342
|
-
expect(td).to.not.be.null;
|
|
343
|
-
expect(td.tags).eql(tags[i], `for ${v}`);
|
|
344
|
-
|
|
345
|
-
td = tracker.getData(data[i]);
|
|
346
|
-
expect(td).to.not.be.null;
|
|
347
|
-
expect(td.tags).eql(tags[i], `for ${v}`);
|
|
348
|
-
}
|
|
349
|
-
});
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
it('.single() has no effect on an array element', function() {
|
|
354
|
-
simulateRequestScope(() => {
|
|
355
|
-
const chain = [
|
|
356
|
-
{ fn: 'has', args: [joi.string()] },
|
|
357
|
-
{ fn: 'single', args: [] }
|
|
358
|
-
];
|
|
359
|
-
|
|
360
|
-
const xyzzy = trackString('xyzzy');
|
|
361
|
-
const tags = makeTags('xyzzy', UNTRUSTED, STRING_TYPE_CHECKED);
|
|
362
|
-
|
|
363
|
-
const schema = makeArraySchema(chain);
|
|
364
|
-
const { value, error } = schema.validate([xyzzy]);
|
|
365
|
-
expect(error).undefined;
|
|
366
|
-
|
|
367
|
-
let td = tracker.getData(xyzzy);
|
|
368
|
-
expect(td).to.not.be.null;
|
|
369
|
-
expect(td.tags).eql(tags);
|
|
370
|
-
|
|
371
|
-
td = tracker.getData(value[0]);
|
|
372
|
-
expect(td).to.not.be.null;
|
|
373
|
-
expect(td.tags).eql(tags);
|
|
374
|
-
});
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
it('.single() propagates tags on a string promoted to an array element', function() {
|
|
378
|
-
simulateRequestScope(() => {
|
|
379
|
-
const chain = [
|
|
380
|
-
{ fn: 'has', args: [joi.string()] },
|
|
381
|
-
{ fn: 'single', args: [] }
|
|
382
|
-
];
|
|
383
|
-
|
|
384
|
-
const xyzzy = trackString('xyzzy');
|
|
385
|
-
const tags = makeTags('xyzzy', UNTRUSTED, STRING_TYPE_CHECKED);
|
|
386
|
-
|
|
387
|
-
const schema = makeArraySchema(chain);
|
|
388
|
-
const { value, error } = schema.validate(xyzzy);
|
|
389
|
-
expect(error).undefined;
|
|
390
|
-
|
|
391
|
-
let td = tracker.getData(xyzzy);
|
|
392
|
-
expect(td).to.not.be.null;
|
|
393
|
-
expect(td.tags).eql(tags);
|
|
394
|
-
|
|
395
|
-
td = tracker.getData(value[0]);
|
|
396
|
-
expect(td).to.not.be.null;
|
|
397
|
-
expect(td.tags).eql(tags);
|
|
398
|
-
});
|
|
399
|
-
});
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
describe('.ordered()', function() {
|
|
403
|
-
const xyzzy = 'xyzzy';
|
|
404
|
-
const plover = 22;
|
|
405
|
-
const twisty = 'twisty-passages';
|
|
406
|
-
const colossal = 44;
|
|
407
|
-
const refData = [xyzzy, plover, twisty, colossal];
|
|
408
|
-
|
|
409
|
-
const orderedPositionTests = [
|
|
410
|
-
// as soon as the first element is validated as a string the
|
|
411
|
-
// remaining elements don't get validated in any way, so they
|
|
412
|
-
// won't get tagged. a single string satisfies both .has() and
|
|
413
|
-
// .items(joi.string().required()).
|
|
414
|
-
{
|
|
415
|
-
chain: [
|
|
416
|
-
{
|
|
417
|
-
fn: 'ordered',
|
|
418
|
-
args: () => [
|
|
419
|
-
joi.string().required(),
|
|
420
|
-
joi.number().required(),
|
|
421
|
-
joi.string(),
|
|
422
|
-
joi.any()
|
|
423
|
-
]
|
|
424
|
-
},
|
|
425
|
-
{ fn: 'max', args: [4] },
|
|
426
|
-
{ fn: 'min', args: [2] },
|
|
427
|
-
{ fn: 'length', args: [4] },
|
|
428
|
-
{ fn: 'has', args: () => [joi.string()] },
|
|
429
|
-
{ fn: 'single', args: [] }
|
|
430
|
-
],
|
|
431
|
-
data: refData,
|
|
432
|
-
tags: [
|
|
433
|
-
makeTags(xyzzy, UNTRUSTED, STRING_TYPE_CHECKED),
|
|
434
|
-
[],
|
|
435
|
-
makeTags(twisty, UNTRUSTED, STRING_TYPE_CHECKED),
|
|
436
|
-
[]
|
|
437
|
-
]
|
|
438
|
-
}
|
|
439
|
-
];
|
|
440
|
-
|
|
441
|
-
// test .ordered() in each chain position by making additional tests.
|
|
442
|
-
const refTest = orderedPositionTests[0];
|
|
443
|
-
for (let i = 1; i < refTest.chain.length; i++) {
|
|
444
|
-
orderedPositionTests[i] = Object.assign({}, refTest);
|
|
445
|
-
orderedPositionTests[i].chain = rotate(refTest.chain, i);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
for (const t of orderedPositionTests) {
|
|
449
|
-
const reducer = (acc, i) => {
|
|
450
|
-
acc.push(i.fn);
|
|
451
|
-
return acc;
|
|
452
|
-
};
|
|
453
|
-
const text = t.chain.reduce(reducer, []).join(', ');
|
|
454
|
-
|
|
455
|
-
it(`works in ${text} order`, function() {
|
|
456
|
-
simulateRequestScope(() => {
|
|
457
|
-
const schema = makeArraySchema(t.chain);
|
|
458
|
-
const data = t.data.map((d) => trackString(d));
|
|
459
|
-
const { value, error } = schema.validate(data);
|
|
460
|
-
|
|
461
|
-
// check each element of the array
|
|
462
|
-
for (let i = 0; i < value.length; i++) {
|
|
463
|
-
const v = value[i];
|
|
464
|
-
const td = tracker.getData(v);
|
|
465
|
-
expect(error).equal(undefined, `${t.fn} for ${v}`);
|
|
466
|
-
if (typeof v === 'string') {
|
|
467
|
-
expect(td).to.not.be.null;
|
|
468
|
-
const tags = t.tags[i];
|
|
469
|
-
expect(td.tags).eql(tags, `${t.fn} for ${v}(${i})`);
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
const orderedErrorTests = [
|
|
477
|
-
// as soon as the first element is validated as a string the
|
|
478
|
-
// remaining elements don't get validated in any way, so they
|
|
479
|
-
// won't get tagged. a single string satisfies both .has() and
|
|
480
|
-
// .items(joi.string().required()).
|
|
481
|
-
{
|
|
482
|
-
name: 'first element fails validation',
|
|
483
|
-
chain: [
|
|
484
|
-
{
|
|
485
|
-
fn: 'ordered',
|
|
486
|
-
args: () => [
|
|
487
|
-
joi.number().required(),
|
|
488
|
-
joi.string().required(),
|
|
489
|
-
joi.string(),
|
|
490
|
-
joi.any()
|
|
491
|
-
]
|
|
492
|
-
}
|
|
493
|
-
],
|
|
494
|
-
data: [() => trackString(xyzzy), () => trackString(twisty), () => 1, () => 1],
|
|
495
|
-
tags: [
|
|
496
|
-
makeTags(xyzzy, UNTRUSTED),
|
|
497
|
-
makeTags(twisty, UNTRUSTED),
|
|
498
|
-
[],
|
|
499
|
-
[]
|
|
500
|
-
]
|
|
501
|
-
},
|
|
502
|
-
{
|
|
503
|
-
name: 'first element passes, second element fails',
|
|
504
|
-
chain: [
|
|
505
|
-
{
|
|
506
|
-
fn: 'ordered',
|
|
507
|
-
args: () => [
|
|
508
|
-
joi.string().required(),
|
|
509
|
-
joi.number().required(),
|
|
510
|
-
joi.string(),
|
|
511
|
-
joi.any()
|
|
512
|
-
]
|
|
513
|
-
}
|
|
514
|
-
],
|
|
515
|
-
data: [() => trackString(xyzzy), () => trackString(twisty), () => 1, () => 1],
|
|
516
|
-
tags: [
|
|
517
|
-
makeTags(xyzzy, UNTRUSTED, STRING_TYPE_CHECKED),
|
|
518
|
-
makeTags(twisty, UNTRUSTED),
|
|
519
|
-
[],
|
|
520
|
-
[]
|
|
521
|
-
]
|
|
522
|
-
},
|
|
523
|
-
{
|
|
524
|
-
name: 'string, any, number, string, then fail',
|
|
525
|
-
chain: [
|
|
526
|
-
{
|
|
527
|
-
fn: 'ordered',
|
|
528
|
-
args: () => [
|
|
529
|
-
joi.string().required(),
|
|
530
|
-
joi.any(),
|
|
531
|
-
joi.number().required(),
|
|
532
|
-
joi.string().required(),
|
|
533
|
-
joi.number()
|
|
534
|
-
]
|
|
535
|
-
}
|
|
536
|
-
],
|
|
537
|
-
data: [() => trackString(xyzzy), () => trackString(twisty), () => 12, () => trackString('z'), () => true],
|
|
538
|
-
tags: [
|
|
539
|
-
makeTags(xyzzy, UNTRUSTED, STRING_TYPE_CHECKED),
|
|
540
|
-
makeTags(twisty, UNTRUSTED),
|
|
541
|
-
[],
|
|
542
|
-
makeTags('z', UNTRUSTED, STRING_TYPE_CHECKED)
|
|
543
|
-
]
|
|
544
|
-
}
|
|
545
|
-
];
|
|
546
|
-
|
|
547
|
-
for (const t of orderedErrorTests) {
|
|
548
|
-
it(`works when ${t.name}`, function() {
|
|
549
|
-
simulateRequestScope(() => {
|
|
550
|
-
t.data = t.data.map((i) => i());
|
|
551
|
-
const schema = makeArraySchema(t.chain);
|
|
552
|
-
const { value, error } = schema.validate(t.data);
|
|
553
|
-
|
|
554
|
-
expect(error).instanceof(Error, `${t.name}`);
|
|
555
|
-
for (let i = 0; i < t.data.length; i++) {
|
|
556
|
-
const v = value[i];
|
|
557
|
-
const td = tracker.getData(v);
|
|
558
|
-
if (typeof v === 'string') {
|
|
559
|
-
expect(td).to.not.be.null;
|
|
560
|
-
expect(td.tags).eql(t.tags[i], `${t.name} for ${v}(${i})`);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
});
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
});
|
|
567
|
-
|
|
568
|
-
describe('.sort', function() {
|
|
569
|
-
const a = 'a';
|
|
570
|
-
const b = 'bb';
|
|
571
|
-
const c = 'ccc';
|
|
572
|
-
const d = 'dddd';
|
|
573
|
-
const e = 'eeeee';
|
|
574
|
-
const z = 'z'.repeat(26);
|
|
575
|
-
|
|
576
|
-
const sortedRefData = [a, b, c, d, e, z];
|
|
577
|
-
|
|
578
|
-
const sortedTests = [
|
|
579
|
-
{
|
|
580
|
-
name: 'propagates with no validation',
|
|
581
|
-
chain: [{ fn: 'sort', args: [] }],
|
|
582
|
-
tags: sortedRefData
|
|
583
|
-
.map((s) => makeTags(s, UNTRUSTED))
|
|
584
|
-
},
|
|
585
|
-
{
|
|
586
|
-
name: 'validates and propagates with .items(joi.string())',
|
|
587
|
-
chain: [
|
|
588
|
-
{ fn: 'sort', args: [] },
|
|
589
|
-
{ fn: 'items', args: () => [joi.string()] }
|
|
590
|
-
],
|
|
591
|
-
tags: sortedRefData
|
|
592
|
-
.map((s) => makeTags(s, UNTRUSTED, STRING_TYPE_CHECKED))
|
|
593
|
-
}
|
|
594
|
-
];
|
|
595
|
-
|
|
596
|
-
for (const t of sortedTests) {
|
|
597
|
-
// build schema and execute the test
|
|
598
|
-
it(`${t.name} for sorted arrays`, function() {
|
|
599
|
-
simulateRequestScope(() => {
|
|
600
|
-
const schema = makeArraySchema(t.chain);
|
|
601
|
-
const { value, error } = schema.validate(sortedRefData.map(i => trackString(i)));
|
|
602
|
-
|
|
603
|
-
expect(error).undefined;
|
|
604
|
-
|
|
605
|
-
for (let i = 0; i < sortedRefData.length; i++) {
|
|
606
|
-
const v = value[i];
|
|
607
|
-
const td = tracker.getData(v);
|
|
608
|
-
expect(td).to.not.be.null;
|
|
609
|
-
expect(td.tags).eql(t.tags[i], `${t.name} for ${v}`);
|
|
610
|
-
}
|
|
611
|
-
});
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
const unsortedRefData = [e, d, c, b, a, z];
|
|
616
|
-
const unsortedTests = [
|
|
617
|
-
{
|
|
618
|
-
name: 'propagates with no validation',
|
|
619
|
-
chain: [
|
|
620
|
-
{
|
|
621
|
-
fn: 'sort',
|
|
622
|
-
args: []
|
|
623
|
-
}
|
|
624
|
-
],
|
|
625
|
-
tags: unsortedRefData.map((s) => makeTags(s, UNTRUSTED))
|
|
626
|
-
},
|
|
627
|
-
{
|
|
628
|
-
name: 'validates and propagates',
|
|
629
|
-
chain: [
|
|
630
|
-
{ fn: 'sort', args: [] },
|
|
631
|
-
{ fn: 'items', args: () => [joi.string()] }
|
|
632
|
-
],
|
|
633
|
-
tags: unsortedRefData.map((s) =>
|
|
634
|
-
makeTags(s, UNTRUSTED, STRING_TYPE_CHECKED)
|
|
635
|
-
)
|
|
636
|
-
}
|
|
637
|
-
];
|
|
638
|
-
|
|
639
|
-
for (const t of unsortedTests) {
|
|
640
|
-
it(`${t.name} for unsorted arrays`, function() {
|
|
641
|
-
simulateRequestScope(() => {
|
|
642
|
-
const schema = makeArraySchema(t.chain);
|
|
643
|
-
const data = unsortedRefData.map(i => trackString(i));
|
|
644
|
-
const { value, error } = schema.validate(data);
|
|
645
|
-
|
|
646
|
-
expect(error).undefined;
|
|
647
|
-
|
|
648
|
-
// get sorted tags to check output array
|
|
649
|
-
const sortedTags = t.tags.slice().sort((a, b) => a[UNTRUSTED][1] - b[UNTRUSTED][1]);
|
|
650
|
-
|
|
651
|
-
for (let i = 0; i < data.length; i++) {
|
|
652
|
-
// check the sorted values
|
|
653
|
-
const v = value[i];
|
|
654
|
-
let td = tracker.getData(v);
|
|
655
|
-
expect(td).to.not.be.null;
|
|
656
|
-
expect(td.tags).eql(sortedTags[i], `${t.name} for ${v}`);
|
|
657
|
-
// check the original unsorted data
|
|
658
|
-
td = tracker.getData(data[i]);
|
|
659
|
-
expect(td).to.not.be.null;
|
|
660
|
-
expect(td.tags).eql(t.tags[i], `${t.name} source ${data[i]}`);
|
|
661
|
-
}
|
|
662
|
-
});
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
});
|
|
666
|
-
|
|
667
|
-
describe('nested arrays', function() {
|
|
668
|
-
const a = 'a';
|
|
669
|
-
const b = 'bb';
|
|
670
|
-
const c = 'ccc';
|
|
671
|
-
const d = 'dddd';
|
|
672
|
-
const e = 'eeeee';
|
|
673
|
-
const refData = [a, [b, c, [d, e]]];
|
|
674
|
-
|
|
675
|
-
function tagger(item, fn = trackString) {
|
|
676
|
-
return (function execute(item) {
|
|
677
|
-
if (Array.isArray(item)) {
|
|
678
|
-
return item.map(execute);
|
|
679
|
-
}
|
|
680
|
-
return fn(item);
|
|
681
|
-
})(item);
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
const tests = [
|
|
685
|
-
{
|
|
686
|
-
name: 'explicit schema',
|
|
687
|
-
chain: [
|
|
688
|
-
{
|
|
689
|
-
fn: 'items',
|
|
690
|
-
args: () => [
|
|
691
|
-
joi.string(),
|
|
692
|
-
joi.array().items(joi.string(), joi.array().items(joi.string()))
|
|
693
|
-
]
|
|
694
|
-
}
|
|
695
|
-
],
|
|
696
|
-
data: () => tagger(refData)
|
|
697
|
-
}
|
|
698
|
-
];
|
|
699
|
-
|
|
700
|
-
for (const t of tests) {
|
|
701
|
-
it(`tags all elements with ${t.name}`, function() {
|
|
702
|
-
simulateRequestScope(() => {
|
|
703
|
-
t.data = t.data();
|
|
704
|
-
const schema = makeArraySchema(t.chain);
|
|
705
|
-
const { value, error } = schema.validate(t.data);
|
|
706
|
-
|
|
707
|
-
expect(error).undefined;
|
|
708
|
-
|
|
709
|
-
const check = (item) => {
|
|
710
|
-
const td = tracker.getData(item);
|
|
711
|
-
expect(td).to.not.be.null;
|
|
712
|
-
expect(td.tags).eql(
|
|
713
|
-
makeTags(item, UNTRUSTED, STRING_TYPE_CHECKED)
|
|
714
|
-
);
|
|
715
|
-
};
|
|
716
|
-
// use tagger to check too
|
|
717
|
-
tagger(value, check);
|
|
718
|
-
});
|
|
719
|
-
});
|
|
720
|
-
}
|
|
721
|
-
});
|
|
722
|
-
|
|
723
|
-
describe('errors', function() {
|
|
724
|
-
const xyzzy = 'xyzzy';
|
|
725
|
-
const twisty = 'twisty-passages';
|
|
726
|
-
const cave = 'cave';
|
|
727
|
-
const blowup = 42;
|
|
728
|
-
const refData = [xyzzy, twisty, cave, blowup];
|
|
729
|
-
|
|
730
|
-
const tests = [
|
|
731
|
-
// as soon as the first element is validated as a string the
|
|
732
|
-
// remaining elements don't get validated in any way, so they
|
|
733
|
-
// won't get tagged. a single string satisfies both .has() and
|
|
734
|
-
// .items(joi.string().required()).
|
|
735
|
-
{
|
|
736
|
-
chain: [
|
|
737
|
-
{ fn: 'items', args: () => [joi.number().forbidden(), joi.string()] }
|
|
738
|
-
],
|
|
739
|
-
data: refData
|
|
740
|
-
},
|
|
741
|
-
{
|
|
742
|
-
chain: [
|
|
743
|
-
{ fn: 'items', args: () => [joi.string(), joi.number().forbidden()] }
|
|
744
|
-
]
|
|
745
|
-
}
|
|
746
|
-
];
|
|
747
|
-
|
|
748
|
-
// make additional tests, moving the number that causes failure.
|
|
749
|
-
const refTest = tests[0];
|
|
750
|
-
for (let i = 1; i < refData.length; i++) {
|
|
751
|
-
tests[i] = {
|
|
752
|
-
chain: refTest.chain,
|
|
753
|
-
data: rotate(refTest.data, i)
|
|
754
|
-
};
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
for (const t of tests) {
|
|
758
|
-
const failIndex = t.data.findIndex((d) => typeof d === 'number');
|
|
759
|
-
|
|
760
|
-
it(`should validate strings before index ${failIndex}`, function() {
|
|
761
|
-
simulateRequestScope(() => {
|
|
762
|
-
const data = t.data.map((d) => trackString(d));
|
|
763
|
-
const schema = makeArraySchema(t.chain);
|
|
764
|
-
const { value, error } = schema.validate(data);
|
|
765
|
-
|
|
766
|
-
expect(error).not.undefined;
|
|
767
|
-
|
|
768
|
-
// strings before the number should have 'string-type-checked'
|
|
769
|
-
for (let i = 0; i < failIndex; i++) {
|
|
770
|
-
const v = value[i];
|
|
771
|
-
const tags = makeTags(v, UNTRUSTED, STRING_TYPE_CHECKED);
|
|
772
|
-
let td = tracker.getData(v);
|
|
773
|
-
expect(td).to.not.be.null;
|
|
774
|
-
expect(td.tags).eql(tags);
|
|
775
|
-
|
|
776
|
-
td = tracker.getData(data[i]);
|
|
777
|
-
expect(td).to.not.be.null;
|
|
778
|
-
expect(td.tags).eql(tags);
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
// strings after the number should have only 'untrusted'
|
|
782
|
-
for (let i = failIndex + 1; i < data.length; i++) {
|
|
783
|
-
const v = value[i];
|
|
784
|
-
const tags = makeTags(v, UNTRUSTED);
|
|
785
|
-
let td = tracker.getData(v);
|
|
786
|
-
expect(td).to.not.be.null;
|
|
787
|
-
expect(td.tags).eql(tags, `for ${v}`);
|
|
788
|
-
|
|
789
|
-
td = tracker.getData(data[i]);
|
|
790
|
-
expect(td).to.not.be.null;
|
|
791
|
-
expect(td.tags).eql(tags, `for ${data[i]}`);
|
|
792
|
-
}
|
|
793
|
-
});
|
|
794
|
-
});
|
|
795
|
-
}
|
|
796
|
-
});
|
|
797
|
-
|
|
798
|
-
describe('async', function() {
|
|
799
|
-
const a = 'a';
|
|
800
|
-
const b = 'bb';
|
|
801
|
-
const c = 'ccc';
|
|
802
|
-
const d = 'dddd';
|
|
803
|
-
const e = 'eeeee';
|
|
804
|
-
const unnestedRefData = [a, b, c, d, e];
|
|
805
|
-
const nestedRefData = [a, [b, c, [d, e]]];
|
|
806
|
-
|
|
807
|
-
function tagger(item, fn = trackString) {
|
|
808
|
-
return (function execute(item) {
|
|
809
|
-
if (Array.isArray(item)) {
|
|
810
|
-
return item.map(execute);
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
return fn(item);
|
|
814
|
-
})(item);
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
function syncValidator() {
|
|
818
|
-
return;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
async function asyncValidator() {
|
|
822
|
-
return new Promise((resolve) => {
|
|
823
|
-
setTimeout(() => {
|
|
824
|
-
resolve();
|
|
825
|
-
}, 10);
|
|
826
|
-
});
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
const tests = [
|
|
830
|
-
{
|
|
831
|
-
name: 'unnested without modification (sync)',
|
|
832
|
-
chain: [
|
|
833
|
-
{ fn: 'items', args: () => [joi.string().external(syncValidator)] }
|
|
834
|
-
],
|
|
835
|
-
data: () => tagger(unnestedRefData),
|
|
836
|
-
tags: [UNTRUSTED, STRING_TYPE_CHECKED, CUSTOM_VALIDATED]
|
|
837
|
-
},
|
|
838
|
-
{
|
|
839
|
-
name: 'pass without modification (async)',
|
|
840
|
-
chain: [
|
|
841
|
-
{
|
|
842
|
-
fn: 'items',
|
|
843
|
-
args: () => [joi.any().external(asyncValidator)]
|
|
844
|
-
}
|
|
845
|
-
],
|
|
846
|
-
data: () => tagger(nestedRefData),
|
|
847
|
-
tags: [UNTRUSTED, CUSTOM_VALIDATED]
|
|
848
|
-
},
|
|
849
|
-
{
|
|
850
|
-
name: 'pass with modification (sync)',
|
|
851
|
-
chain: [
|
|
852
|
-
{
|
|
853
|
-
fn: 'items',
|
|
854
|
-
args: () => [joi.any().external(syncValidator)]
|
|
855
|
-
}
|
|
856
|
-
],
|
|
857
|
-
data: () => tagger(nestedRefData),
|
|
858
|
-
tags: [UNTRUSTED, CUSTOM_VALIDATED]
|
|
859
|
-
}
|
|
860
|
-
];
|
|
861
|
-
|
|
862
|
-
for (const t of tests) {
|
|
863
|
-
it(`tags all elements with ${t.name}`, function(done) {
|
|
864
|
-
simulateRequestScope(() => {
|
|
865
|
-
t.data = t.data();
|
|
866
|
-
const schema = makeArraySchema(t.chain);
|
|
867
|
-
schema.validateAsync(t.data)
|
|
868
|
-
.then((value) => {
|
|
869
|
-
const check = (item) => {
|
|
870
|
-
const td = tracker.getData(item);
|
|
871
|
-
expect(td).to.not.be.null;
|
|
872
|
-
expect(td.tags).to.deep.equal(makeTags(item, ...t.tags));
|
|
873
|
-
};
|
|
874
|
-
// use tagger to check too
|
|
875
|
-
tagger(value, check);
|
|
876
|
-
tagger(t.data, check);
|
|
877
|
-
done();
|
|
878
|
-
})
|
|
879
|
-
.catch(err => done(err));
|
|
880
|
-
});
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
|
-
});
|
|
884
|
-
|
|
885
|
-
//
|
|
886
|
-
// helpers
|
|
887
|
-
//
|
|
888
|
-
function rotate(a, n) {
|
|
889
|
-
if (a.length === 0 || n === 0) {
|
|
890
|
-
return a;
|
|
891
|
-
}
|
|
892
|
-
n = n % a.length;
|
|
893
|
-
return [...a.slice(n), ...a.slice(0, n)];
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
function permutations(arr, n = arr.length, out = []) {
|
|
897
|
-
for (let i = 0; i < n; i++) {
|
|
898
|
-
// if down to one remaing just use it
|
|
899
|
-
if (n === 2) {
|
|
900
|
-
out.push(arr.slice());
|
|
901
|
-
} else {
|
|
902
|
-
permutations(arr, n - 1, out);
|
|
903
|
-
}
|
|
904
|
-
const j = n % 2 == 0 ? i : 0;
|
|
905
|
-
const t = arr[n - 1];
|
|
906
|
-
arr[n - 1] = arr[j];
|
|
907
|
-
arr[j] = t;
|
|
908
|
-
}
|
|
909
|
-
return out;
|
|
910
|
-
}
|
|
911
|
-
});
|
|
912
|
-
|