@contrast/protect 1.54.0 → 1.54.2

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.
Files changed (58) hide show
  1. package/package.json +14 -11
  2. package/lib/error-handlers/common-handler.test.js +0 -52
  3. package/lib/error-handlers/index.test.js +0 -32
  4. package/lib/error-handlers/init-domain.test.js +0 -22
  5. package/lib/error-handlers/install/express.test.js +0 -290
  6. package/lib/error-handlers/install/fastify.test.js +0 -130
  7. package/lib/error-handlers/install/hapi.test.js +0 -102
  8. package/lib/error-handlers/install/koa2.test.js +0 -83
  9. package/lib/error-handlers/install/restify.test.js +0 -57
  10. package/lib/get-source-context.test.js +0 -35
  11. package/lib/hardening/handlers.test.js +0 -89
  12. package/lib/hardening/index.test.js +0 -31
  13. package/lib/hardening/install/node-serialize0.test.js +0 -58
  14. package/lib/index.test.js +0 -53
  15. package/lib/input-analysis/handlers.test.js +0 -1604
  16. package/lib/input-analysis/index.test.js +0 -45
  17. package/lib/input-analysis/install/body-parser1.test.js +0 -134
  18. package/lib/input-analysis/install/busboy1.test.js +0 -81
  19. package/lib/input-analysis/install/cookie-parser1.test.js +0 -144
  20. package/lib/input-analysis/install/express.test.js +0 -241
  21. package/lib/input-analysis/install/fastify.test.js +0 -96
  22. package/lib/input-analysis/install/formidable1.test.js +0 -114
  23. package/lib/input-analysis/install/hapi.test.js +0 -292
  24. package/lib/input-analysis/install/http.test.js +0 -270
  25. package/lib/input-analysis/install/koa-body5.test.js +0 -92
  26. package/lib/input-analysis/install/koa-bodyparser4.test.js +0 -92
  27. package/lib/input-analysis/install/koa2.test.js +0 -259
  28. package/lib/input-analysis/install/multer1.test.js +0 -209
  29. package/lib/input-analysis/install/qs6.test.js +0 -79
  30. package/lib/input-analysis/install/restify.test.js +0 -98
  31. package/lib/input-analysis/install/universal-cookie4.test.js +0 -70
  32. package/lib/input-analysis/ip-analysis.test.js +0 -71
  33. package/lib/input-analysis/virtual-patches.test.js +0 -106
  34. package/lib/input-tracing/handlers/index.test.js +0 -1237
  35. package/lib/input-tracing/index.test.js +0 -62
  36. package/lib/input-tracing/install/child-process.test.js +0 -133
  37. package/lib/input-tracing/install/eval.test.js +0 -78
  38. package/lib/input-tracing/install/fs.test.js +0 -108
  39. package/lib/input-tracing/install/function.test.js +0 -81
  40. package/lib/input-tracing/install/http.test.js +0 -85
  41. package/lib/input-tracing/install/http2.test.js +0 -83
  42. package/lib/input-tracing/install/marsdb.test.js +0 -126
  43. package/lib/input-tracing/install/mongodb.test.js +0 -280
  44. package/lib/input-tracing/install/mssql.test.js +0 -81
  45. package/lib/input-tracing/install/mysql.test.js +0 -108
  46. package/lib/input-tracing/install/postgres.test.js +0 -117
  47. package/lib/input-tracing/install/sequelize.test.js +0 -78
  48. package/lib/input-tracing/install/spdy.test.js +0 -76
  49. package/lib/input-tracing/install/sqlite3.test.js +0 -88
  50. package/lib/input-tracing/install/vm.test.js +0 -176
  51. package/lib/make-response-blocker.test.js +0 -99
  52. package/lib/make-source-context.test.js +0 -219
  53. package/lib/policy.test.js +0 -446
  54. package/lib/semantic-analysis/handlers.test.js +0 -379
  55. package/lib/semantic-analysis/index.test.js +0 -38
  56. package/lib/semantic-analysis/install/libxmljs.test.js +0 -156
  57. package/lib/semantic-analysis/utils/xml-analysis.test.js +0 -156
  58. package/lib/throw-security-exception.test.js +0 -37
@@ -1,1237 +0,0 @@
1
- 'use strict';
2
-
3
- const { expect } = require('chai');
4
- const agentLib = require('@contrast/agent-lib');
5
- const mocks = require('@contrast/test/mocks');
6
- const { agentLibIDListTypes } = require('@contrast/common');
7
-
8
- describe('protect input-tracing handlers', function () {
9
- let handlers;
10
-
11
- // each test should be asserting this at least
12
- const TEST_DESCRIPTION = 'captures findings and sinkContext in result.exploitMetadata array';
13
-
14
- // for additional assertions that might happen in tests, add to description
15
- function makeTestDescription(blocks) {
16
- const addl = blocks ? ' and blocks when in BLOCK mode' : '';
17
- return `${TEST_DESCRIPTION}${addl}`;
18
- }
19
-
20
- function makeSourceContext(resultsList, ruleModes) {
21
- const resultsMap = Object.create(null);
22
-
23
- if (resultsList) {
24
- for (const result of resultsList) {
25
- if (!resultsMap[result.ruleId]) {
26
- resultsMap[result.ruleId] = [];
27
- }
28
- resultsMap[result.ruleId].push(result);
29
- }
30
- }
31
-
32
- const testRuleIds = [
33
- 'cmd-injection',
34
- 'path-traversal',
35
- 'sql-injection',
36
- 'ssjs-injection',
37
- 'nosql-injection',
38
- 'nosql-injection-mongo',
39
- 'reflected-xss',
40
- ];
41
-
42
- return {
43
- policy: {
44
- rulesMask: testRuleIds.reduce((acc, ruleId) => acc | agentLib.constants.RuleType[ruleId]),
45
- ...testRuleIds.reduce((acc, ruleId) => ({ ...acc, [ruleId]: 'monitor' }), {}),
46
- ...ruleModes,
47
- },
48
- trackRequest: true,
49
- resultsMap,
50
- };
51
- }
52
-
53
- beforeEach(function () {
54
- const core = mocks.core();
55
- core.logger = mocks.logger();
56
- core.protect = mocks.protect();
57
- core.protect.throwSecurityException = require('../../throw-security-exception');
58
-
59
- handlers = require('.')(core);
60
- });
61
-
62
- describe('handlePathTraversal()', function () {
63
- const sinkContextPositive = {
64
- name: 'fs.readFileSync',
65
- value: './../../../etc/passwd',
66
- stack: []
67
- };
68
- const sinkContextNegative = {
69
- name: 'fs.readFileSync',
70
- value: 'index.js',
71
- stack: []
72
- };
73
- const findings = {
74
- path: './../../../etc/passwd',
75
- };
76
-
77
- [
78
- {
79
- blocks: false,
80
- sourceContext: makeSourceContext(
81
- [{
82
- ruleId: 'path-traversal',
83
- value: '../../../etc/passwd',
84
- exploitMetadata: [],
85
- }]
86
- ),
87
- expectedDetails: [
88
- {
89
- sinkContext: sinkContextPositive,
90
- findings,
91
- },
92
- {
93
- sinkContext: sinkContextPositive,
94
- findings,
95
- }
96
- ]
97
- },
98
- {
99
- blocks: true,
100
- sourceContext: makeSourceContext(
101
- [{
102
- ruleId: 'path-traversal',
103
- value: '../../../etc/passwd',
104
- exploitMetadata: [],
105
- }],
106
- {
107
- 'path-traversal': 'block'
108
- }
109
- ),
110
- expectedDetails: [
111
- {
112
- sinkContext: sinkContextPositive,
113
- findings,
114
- },
115
- ]
116
- },
117
- {
118
- blocks: false,
119
- sourceContext: makeSourceContext(
120
- [{
121
- ruleId: 'path-traversal',
122
- value: '../../../etc/passwd',
123
- exploitMetadata: [],
124
- }],
125
- {
126
- ['path-traversal']: 'off'
127
- }
128
- ),
129
- expectedDetails: []
130
- }
131
- ].forEach(({ blocks, sourceContext, expectedDetails }) => {
132
- it(makeTestDescription(blocks), function () {
133
- const testFn = () => {
134
- handlers.handlePathTraversal(sourceContext, sinkContextPositive);
135
- handlers.handlePathTraversal(sourceContext, sinkContextPositive);
136
- handlers.handlePathTraversal(sourceContext, sinkContextNegative);
137
- };
138
-
139
- const test = expect(testFn);
140
- blocks ? test.to.throw('SecurityException') : test.not.to.throw();
141
-
142
- expect(sourceContext.resultsMap['path-traversal'][0].exploitMetadata).to.deep.equal(expectedDetails);
143
- });
144
- });
145
- });
146
-
147
- describe('handleCmdInjection()', function () {
148
- const sinkContextPositive = {
149
- name: 'child_process.execSync',
150
- value: 'ls; cat /etc/passwd',
151
- stack: [],
152
- };
153
- const sinkContextNegative = {
154
- name: 'child_process.execSync',
155
- value: 'foo',
156
- stack: []
157
- };
158
- const findings = {
159
- boundaryIndex: 2,
160
- endIndex: 19,
161
- overrunIndex: 4,
162
- startIndex: 2,
163
- };
164
-
165
- [
166
- {
167
- blocks: false,
168
- sourceContext: makeSourceContext(
169
- [{
170
- ruleId: 'cmd-injection',
171
- value: '; cat /etc/passwd',
172
- exploitMetadata: [],
173
- }]
174
- ),
175
- expectedDetails: [
176
- {
177
- sinkContext: sinkContextPositive,
178
- findings,
179
- },
180
- {
181
- sinkContext: sinkContextPositive,
182
- findings,
183
- }
184
- ]
185
- },
186
- {
187
- blocks: true,
188
- sourceContext: makeSourceContext(
189
- [{
190
- ruleId: 'cmd-injection',
191
- value: '; cat /etc/passwd',
192
- exploitMetadata: [],
193
- }], {
194
- ['cmd-injection']: 'block'
195
- }
196
- ),
197
- expectedDetails: [
198
- {
199
- sinkContext: sinkContextPositive,
200
- findings,
201
- },
202
- ]
203
- },
204
- {
205
- blocks: false,
206
- sourceContext: makeSourceContext(
207
- [{
208
- ruleId: 'cmd-injection',
209
- value: '; cat /etc/passwd',
210
- exploitMetadata: [],
211
- }], {
212
- ['cmd-injection']: 'off'
213
- }
214
- ),
215
- expectedDetails: []
216
- }
217
- ].forEach(({ blocks, sourceContext, expectedDetails }) => {
218
- it(makeTestDescription(blocks), function () {
219
- const testFn = () => {
220
- handlers.handleCommandInjection(sourceContext, sinkContextPositive);
221
- handlers.handleCommandInjection(sourceContext, sinkContextPositive);
222
- handlers.handleCommandInjection(sourceContext, sinkContextNegative);
223
- };
224
-
225
- const test = expect(testFn);
226
- blocks ? test.to.throw('SecurityException') : test.not.to.throw();
227
-
228
- const cmdiResults = sourceContext.resultsMap['cmd-injection'];
229
-
230
- expect(cmdiResults[0].exploitMetadata).to.deep.equal(expectedDetails);
231
- });
232
- });
233
- });
234
-
235
- describe('handleSqlInjection()', function () {
236
- const sinkContextPositive = {
237
- name: 'mysql.query',
238
- // 0123456789*123456789*123456789*123456789*123456789*123456789*1234567890123
239
- value: 'select * from foo where col = """ and 1 = 1; --" or col = "" and 1 = 1; --',
240
- stack: [],
241
- };
242
- const sinkContextNegative = {
243
- name: 'mysql.query',
244
- value: 'select * from foo where col = """ and 1 = 1; --"',
245
- stack: [],
246
- };
247
- const findings = {
248
- boundaryIndex: 58,
249
- endIndex: 74,
250
- overrunIndex: 60,
251
- startIndex: 59,
252
- };
253
-
254
- [
255
- {
256
- blocks: false,
257
- findings,
258
- sourceContext: makeSourceContext(
259
- [
260
- {
261
- ruleId: 'sql-injection',
262
- value: '" and 1 = 1; --',
263
- exploitMetadata: [],
264
- }
265
- ]
266
- ),
267
- expectedDetails: [
268
- {
269
- sinkContext: sinkContextPositive,
270
- findings,
271
- },
272
- {
273
- sinkContext: sinkContextPositive,
274
- findings,
275
- }
276
- ]
277
- },
278
- {
279
- blocks: true,
280
- findings: {
281
- startIndex: 0,
282
- endIndex: 30,
283
- overrunIndex: 0,
284
- boundaryIndex: 0,
285
- },
286
- sourceContext: makeSourceContext(
287
- [
288
- {
289
- ruleId: 'sql-injection',
290
- // this is the exact value of the query - this is to get coverage
291
- value: 'select * from foo where col = """ and 1 = 1; --" or col = "" and 1 = 1; --',
292
- exploitMetadata: [],
293
- }
294
- ],
295
- {
296
- ['sql-injection']: 'block'
297
- }
298
- ),
299
- expectedDetails: [
300
- {
301
- sinkContext: sinkContextPositive,
302
- findings: {
303
- startIndex: 0,
304
- endIndex: 73,
305
- overrunIndex: 0,
306
- boundaryIndex: 0,
307
- },
308
- },
309
- ]
310
- },
311
- {
312
- blocks: false,
313
- findings: {
314
- startIndex: 0,
315
- endIndex: 30,
316
- overrunIndex: 0,
317
- boundaryIndex: 0,
318
- },
319
- sourceContext: makeSourceContext(
320
- [
321
- {
322
- ruleId: 'sql-injection',
323
- value: '" and 1 = 1; --',
324
- exploitMetadata: [],
325
- }
326
- ],
327
- {
328
- ['sql-injection']: 'off'
329
- }
330
- ),
331
- expectedDetails: []
332
- }
333
- ].forEach(({ blocks, sourceContext, expectedDetails }) => {
334
- it(makeTestDescription(blocks), function () {
335
- const testFn = () => {
336
- handlers.handleSqlInjection(sourceContext, sinkContextPositive);
337
- handlers.handleSqlInjection(sourceContext, sinkContextPositive);
338
- handlers.handleSqlInjection(sourceContext, sinkContextNegative);
339
- };
340
-
341
- const test = expect(testFn);
342
- blocks ? test.to.throw('SecurityException') : test.not.to.throw();
343
-
344
- const sqliResults = sourceContext.resultsMap['sql-injection'];
345
- expect(sqliResults[0].exploitMetadata).to.deep.equal(expectedDetails);
346
- });
347
- });
348
- });
349
-
350
- describe('nosqlInjectionMongo()', function () {
351
- const expansionSinkContextPositive = {
352
- name: 'mongodb.find',
353
- value: { id: { '$ne': 0 }, name: 'testValue' },
354
- stack: [],
355
- };
356
- const expansionSinkContextNegative = {
357
- name: 'mongodb.find',
358
- value: { id: 1 },
359
- stack: [],
360
- };
361
- const expansionSinkContextWrong = {
362
- name: 'mongodb.find',
363
- value: '{ id: { "$ne": 0 }, name: "testValue" }',
364
- stack: [],
365
- };
366
- const expansionFindings = {
367
- start: 8,
368
- end: 11,
369
- inputBoundaryIndex: 0,
370
- boundaryOverrunIndex: 8
371
- };
372
- const stringSinkContextPositive = {
373
- name: 'mongodb.Db.eval',
374
- value: 'function() { console.log(`function() { return "hi"; }`) (function() { return "hi"; })() }',
375
- stack: [],
376
- };
377
- const stringSinkContextNegative = {
378
- name: 'mongodb.Db.eval',
379
- value: 'function() { console.log(`function() { return "hi"; }`) }',
380
- stack: [],
381
- };
382
- const stringSinkContextWrong = {
383
- name: 'mongodb.Db.eval',
384
- value: { id: { '$ne': 0 }, name: 'testValue' },
385
- stack: [],
386
- };
387
- const stringFindings = {
388
- start: 58,
389
- end: 84,
390
- boundaryOverrunIndex: 0,
391
- inputBoundaryIndex: 0,
392
- };
393
-
394
- [
395
- {
396
- blocks: false,
397
- type: 'expansionInjection',
398
- findings: expansionFindings,
399
- sourceContext: makeSourceContext(
400
- [
401
- {
402
- ruleId: 'nosql-injection-mongo',
403
- value: { '$ne': 0 },
404
- key: '$ne',
405
- exploitMetadata: [],
406
- mongoContext: { inputToCheck: 0 },
407
- }
408
- ]
409
- ),
410
- expectedDetails: [
411
- {
412
- sinkContext: expansionSinkContextPositive,
413
- findings: expansionFindings,
414
- },
415
- {
416
- sinkContext: expansionSinkContextPositive,
417
- findings: expansionFindings,
418
- }
419
- ]
420
- },
421
- {
422
- blocks: true,
423
- type: 'expansionInjection',
424
- findings: expansionFindings,
425
- sourceContext: makeSourceContext(
426
- [
427
- {
428
- ruleId: 'nosql-injection-mongo',
429
- // this is the exact value of the query - this is to get coverage
430
- value: { '$ne': 0 },
431
- key: '$ne',
432
- exploitMetadata: [],
433
- mongoContext: { inputToCheck: 0 },
434
- }
435
- ],
436
- {
437
- ['nosql-injection-mongo']: 'block'
438
- }
439
- ),
440
- expectedDetails: [
441
- {
442
- sinkContext: expansionSinkContextPositive,
443
- findings: expansionFindings,
444
- },
445
- ]
446
- },
447
- {
448
- blocks: false,
449
- type: 'expansionInjection',
450
- findings: expansionFindings,
451
- sourceContext: makeSourceContext(
452
- [
453
- {
454
- ruleId: 'nosql-injection-mongo',
455
- value: { '$ne': 0 },
456
- key: '$ne',
457
- exploitMetadata: [],
458
- mongoContext: { inputToCheck: 0 },
459
- }
460
- ],
461
- {
462
- ['nosql-injection-mongo']: 'off'
463
- }
464
- ),
465
- expectedDetails: []
466
- },
467
- {
468
- blocks: false,
469
- type: 'stringInjection',
470
- stringFindings,
471
- sourceContext: makeSourceContext(
472
- [
473
- {
474
- ruleId: 'ssjs-injection',
475
- value: 'function() { return "hi"; }',
476
- exploitMetadata: [],
477
- }
478
- ]
479
- ),
480
- expectedDetails: [
481
- {
482
- sinkContext: stringSinkContextPositive,
483
- findings: stringFindings,
484
- },
485
- {
486
- sinkContext: stringSinkContextPositive,
487
- findings: stringFindings,
488
- }
489
- ]
490
- },
491
- {
492
- blocks: true,
493
- type: 'stringInjection',
494
- findings: stringFindings,
495
- sourceContext: makeSourceContext(
496
- [
497
- {
498
- ruleId: 'ssjs-injection',
499
- value: 'function() { return "hi"; }',
500
- exploitMetadata: [],
501
- }
502
- ],
503
- {
504
- ['nosql-injection-mongo']: 'block'
505
- }
506
- ),
507
- expectedDetails: [
508
- {
509
- sinkContext: stringSinkContextPositive,
510
- findings: stringFindings,
511
- },
512
- ]
513
- },
514
- {
515
- blocks: false,
516
- type: 'stringInjection',
517
- findings: stringFindings,
518
- sourceContext: makeSourceContext(
519
- [
520
- {
521
- ruleId: 'ssjs-injection',
522
- value: 'function() { return "hi"; }',
523
- exploitMetadata: [],
524
- }
525
- ],
526
- {
527
- ['ssjs-injection']: 'off'
528
- }
529
- ),
530
- expectedDetails: []
531
- },
532
- ].forEach(({ blocks, type, sourceContext, expectedDetails }) => {
533
- it(makeTestDescription(blocks), function () {
534
- const testFn = () => {
535
- if (type == 'expansionInjection') {
536
- handlers.nosqlInjectionMongo(sourceContext, expansionSinkContextPositive);
537
- handlers.nosqlInjectionMongo(sourceContext, expansionSinkContextPositive);
538
- handlers.nosqlInjectionMongo(sourceContext, expansionSinkContextNegative);
539
- handlers.nosqlInjectionMongo(sourceContext, expansionSinkContextWrong);
540
- } else {
541
- handlers.nosqlInjectionMongo(sourceContext, stringSinkContextPositive);
542
- handlers.nosqlInjectionMongo(sourceContext, stringSinkContextPositive);
543
- handlers.nosqlInjectionMongo(sourceContext, stringSinkContextNegative);
544
- handlers.nosqlInjectionMongo(sourceContext, stringSinkContextWrong);
545
- }
546
- };
547
-
548
- const test = expect(testFn);
549
- blocks ? test.to.throw('SecurityException') : test.not.to.throw();
550
-
551
- const nosqliResults = sourceContext.resultsMap['nosql-injection-mongo'];
552
- const ssjsResult = sourceContext.resultsMap['ssjs-injection'];
553
- if (type == 'expansionInjection') {
554
- expect(nosqliResults[0].exploitMetadata).to.deep.equal(expectedDetails);
555
- } else {
556
- expect(ssjsResult[0].exploitMetadata).to.deep.equal(expectedDetails);
557
- }
558
- });
559
- });
560
-
561
- [
562
- {
563
- userInput: 'function() ',
564
- cmd: 'function() { return true; }',
565
- start: 0,
566
- end: 10,
567
- },
568
- {
569
- userInput: '(function() { return true })()',
570
- cmd: 'function() { return (function() { return true })(); }',
571
- start: 20,
572
- end: 49,
573
- }
574
- ].forEach(({ userInput, cmd, start, end }, index) => {
575
- it(`sourceContext-injection as function execution - case ${index + 1}`, function () {
576
- const sinkContexts = [
577
- {
578
- value: { $where: cmd },
579
- name: 'mongodb.query'
580
- },
581
- {
582
- value: {
583
- $expr: {
584
- $function: {
585
- body: cmd,
586
- args: [],
587
- lang: 'js',
588
- }
589
- }
590
- },
591
- name: 'mongodb.query'
592
- },
593
- ];
594
-
595
- sinkContexts.forEach(sc => {
596
- const data = {
597
- sourceContext: makeSourceContext(
598
- [
599
- {
600
- ruleId: 'ssjs-injection',
601
- value: userInput,
602
- idsList: [],
603
- exploitMetadata: [],
604
- }
605
- ]
606
- ),
607
- };
608
-
609
- handlers.nosqlInjectionMongo(data.sourceContext, sc);
610
- expect(data.sourceContext.resultsMap['nosql-injection-mongo']).to.have.lengthOf(1);
611
- expect(data.sourceContext.resultsMap['ssjs-injection']).to.have.lengthOf(1);
612
- expect(data.sourceContext.resultsMap['nosql-injection-mongo'][0]).to.eql({
613
- ruleId: 'nosql-injection-mongo',
614
- value: userInput,
615
- idsList: [],
616
- exploitMetadata: [
617
- {
618
- sinkContext: sc,
619
- findings: {
620
- start,
621
- end,
622
- boundaryOverrunIndex: 0,
623
- inputBoundaryIndex: 0
624
- }
625
- }
626
- ],
627
- mappedId: 'nosql-injection-mongo'
628
- });
629
- });
630
- });
631
- });
632
-
633
- it('sourceContext-injection as function execution - $accumulator', function () {
634
- const sinkContext = {
635
- value: {
636
- $group: {
637
- _id: null,
638
- count: {
639
- $accumulator: {
640
- init: 'function() { return { count: 0 } }',
641
- accumulate: 'function(state) { return { count: state.count + 1 } }',
642
- merge: 'function(state1, state2) { return { count: state1.count + state2.count } }',
643
- finalize: 'function(state) { return state; }',
644
- lang: 'js',
645
- }
646
- }
647
- }
648
- },
649
- name: 'mongodb.query'
650
- };
651
-
652
- [
653
- {
654
- value: 'function() { return { count: 0 } }',
655
- findings: {
656
- boundaryOverrunIndex: 0,
657
- end: 33,
658
- inputBoundaryIndex: 0,
659
- start: 0,
660
- }
661
- },
662
- {
663
- value: 'function(state) { return { count: state.count + 1 } }',
664
- findings: {
665
- boundaryOverrunIndex: 0,
666
- end: 52,
667
- inputBoundaryIndex: 0,
668
- start: 0,
669
- }
670
- },
671
- {
672
- value: 'function(state1, state2) { return { count: state1.count + state2.count } }',
673
- findings: {
674
- boundaryOverrunIndex: 0,
675
- end: 73,
676
- inputBoundaryIndex: 0,
677
- start: 0,
678
- }
679
- },
680
- {
681
- value: 'function(state) { return state; }',
682
- findings: {
683
- boundaryOverrunIndex: 0,
684
- end: 32,
685
- inputBoundaryIndex: 0,
686
- start: 0,
687
- }
688
- },
689
- ].forEach((obj) => {
690
- [sinkContext, Object.assign({}, sinkContext, { value: [sinkContext.value] })]
691
- .forEach(sc => {
692
- const sourceContext = makeSourceContext(
693
- [
694
- {
695
- ruleId: 'ssjs-injection',
696
- value: obj.value,
697
- exploitMetadata: [],
698
- }
699
- ]
700
- );
701
-
702
- handlers.nosqlInjectionMongo(sourceContext, sc);
703
- expect(sourceContext.resultsMap['nosql-injection-mongo']).to.have.lengthOf(1);
704
- expect(sourceContext.resultsMap['ssjs-injection']).to.have.lengthOf(1);
705
- expect(sourceContext.resultsMap['nosql-injection-mongo'][0]).to.eql({
706
- ruleId: 'nosql-injection-mongo',
707
- value: obj.value,
708
- exploitMetadata: [
709
- {
710
- sinkContext: sc,
711
- findings: obj.findings,
712
- }
713
- ],
714
- mappedId: 'nosql-injection-mongo'
715
- });
716
- });
717
- });
718
- });
719
-
720
- it('sourceContext-injection as function execution - not an attack', function () {
721
- const data = {
722
- sourceContext: makeSourceContext(
723
- [
724
- {
725
- ruleId: 'ssjs-injection',
726
- value: ' ',
727
- idsList: [],
728
- exploitMetadata: [],
729
- }
730
- ]
731
- ),
732
- };
733
-
734
- const sinkContext = {
735
- value: { $where: 'function() { return true; } ' },
736
- };
737
-
738
- handlers.nosqlInjectionMongo(data.sourceContext, sinkContext);
739
- expect(data.sourceContext.resultsMap['nosql-injection-mongo']).to.be.undefined;
740
- expect(data.sourceContext.resultsMap['ssjs-injection']).to.have.lengthOf(1);
741
- });
742
-
743
- it(`nosql-injection-mongo - ${agentLibIDListTypes.TRUE_CLAUSE_1} - object`, function () {
744
- const vector = "' || true || '";
745
-
746
- const sinkContext = {
747
- name: 'mongodb.FindCursor',
748
- value: {
749
- $where: `this.orderId === '${vector}'`
750
- }
751
- };
752
-
753
- const expectedDetails = {
754
- start: 18,
755
- end: 31,
756
- boundaryOverrunIndex: 0,
757
- inputBoundaryIndex: 0,
758
- };
759
-
760
- const data = {
761
- blocks: true,
762
- type: 'expansionInjection',
763
- findings: expansionFindings,
764
- sourceContext: makeSourceContext(
765
- [
766
- {
767
- ruleId: 'nosql-injection-mongo',
768
- inputType: 'JsonValue',
769
- path: [
770
- 'input',
771
- ],
772
- key: 'input',
773
- value: '\' || true || \'',
774
- score: 10,
775
- idsList: [
776
- agentLibIDListTypes.TRUE_CLAUSE_1,
777
- ],
778
- mappedId: 'nosql-injection',
779
- blocked: false,
780
- exploitMetadata: [
781
- ],
782
- }
783
- ],
784
- {
785
- ['nosql-injection-mongo']: 'block'
786
- }
787
- ),
788
- expectedDetails: [
789
- {
790
- sinkContext: {
791
- name: 'mongodb.FindCursor',
792
- value: {
793
- $where: `this.orderId === '${vector}'`,
794
- }
795
- },
796
- findings: expansionFindings,
797
- },
798
- ]
799
- };
800
-
801
- const test = expect(() => handlers.nosqlInjectionMongo(data.sourceContext, sinkContext));
802
- test.to.throw('SecurityException');
803
-
804
- const nosqliResults = data.sourceContext.resultsMap['nosql-injection-mongo'];
805
- expect(nosqliResults[0].exploitMetadata).to.deep.equal([
806
- {
807
- sinkContext: {
808
- name: 'mongodb.FindCursor',
809
- value: {
810
- $where: `this.orderId === '${vector}'`,
811
- },
812
- stack: [],
813
- },
814
- findings: expectedDetails,
815
- },
816
- ]);
817
- });
818
-
819
- it(`nosql-injection-mongo - ${agentLibIDListTypes.MONGO_SLEEP} - object`, function () {
820
- const sinkContext = {
821
- name: 'mongodb.FindCursor',
822
- value: {
823
- $where: 'this.title ==sleep(500)',
824
- }
825
- };
826
-
827
- const expectedDetails = {
828
- start: 13,
829
- end: 22,
830
- boundaryOverrunIndex: 0,
831
- inputBoundaryIndex: 0,
832
- };
833
-
834
- const data = {
835
- blocks: true,
836
- type: 'expansionInjection',
837
- findings: expansionFindings,
838
- sourceContext: makeSourceContext(
839
- [
840
- {
841
- ruleId: 'nosql-injection-mongo',
842
- inputType: 'JsonValue',
843
- path: [
844
- 'input',
845
- ],
846
- key: 'input',
847
- value: 'sleep(500)',
848
- score: 10,
849
- idsList: [
850
- agentLibIDListTypes.MONGO_SLEEP,
851
- ],
852
- mappedId: 'nosql-injection',
853
- blocked: false,
854
- exploitMetadata: [
855
- ],
856
- }
857
- ],
858
- {
859
- ['nosql-injection-mongo']: 'block'
860
- }
861
- ),
862
- expectedDetails: [
863
- {
864
- sinkContext: {
865
- name: 'mongodb.FindCursor',
866
- value: {
867
- $where: 'this.title ==sleep(500)',
868
- }
869
- },
870
- findings: expansionFindings,
871
- },
872
- ]
873
- };
874
-
875
- const test = expect(() => handlers.nosqlInjectionMongo(data.sourceContext, sinkContext));
876
- test.to.throw('SecurityException');
877
-
878
- const nosqliResults = data.sourceContext.resultsMap['nosql-injection-mongo'];
879
- expect(nosqliResults[0].exploitMetadata).to.deep.equal([
880
- {
881
- sinkContext: {
882
- name: 'mongodb.FindCursor',
883
- value: {
884
- $where: 'this.title ==sleep(500)',
885
- },
886
- stack: [
887
- ],
888
- },
889
- findings: expectedDetails,
890
- },
891
- ]);
892
- });
893
-
894
- it(`nosql-injection-mongo - ${agentLibIDListTypes.MONGO_SLEEP} - string`, function () {
895
- const sinkContext = {
896
- name: 'mongodb.FindCursor',
897
- value: 'sleep(500)'
898
- };
899
-
900
- const expectedDetails = {
901
- start: 0,
902
- end: 9,
903
- boundaryOverrunIndex: 0,
904
- inputBoundaryIndex: 0,
905
- };
906
-
907
- const data = {
908
- blocks: true,
909
- type: 'expansionInjection',
910
- findings: expansionFindings,
911
- sourceContext: makeSourceContext(
912
- [
913
- {
914
- ruleId: 'nosql-injection-mongo',
915
- inputType: 'JsonValue',
916
- path: [
917
- 'input',
918
- ],
919
- key: 'input',
920
- value: 'sleep(500)',
921
- score: 10,
922
- idsList: [
923
- agentLibIDListTypes.MONGO_SLEEP,
924
- ],
925
- mappedId: 'nosql-injection',
926
- blocked: false,
927
- exploitMetadata: [
928
- ],
929
- }
930
- ],
931
- {
932
- ['nosql-injection-mongo']: 'block'
933
- }
934
- ),
935
- expectedDetails: [
936
- {
937
- sinkContext: {
938
- name: 'mongodb.FindCursor',
939
- value: 'sleep(500)'
940
- },
941
- findings: expansionFindings,
942
- },
943
- ]
944
- };
945
-
946
- const test = expect(() => handlers.nosqlInjectionMongo(data.sourceContext, sinkContext));
947
- test.to.throw('SecurityException');
948
-
949
- const nosqliResults = data.sourceContext.resultsMap['nosql-injection-mongo'];
950
- expect(nosqliResults[0].exploitMetadata).to.deep.equal([
951
- {
952
- sinkContext: {
953
- name: 'mongodb.FindCursor',
954
- value: 'sleep(500)',
955
- stack: [],
956
- },
957
- findings: expectedDetails,
958
- },
959
- ]);
960
- });
961
- });
962
-
963
- describe('ssjsInjection()', function () {
964
- const stringSinkContextPositive = {
965
- name: 'vm.Script',
966
- value: 'function() { console.log("function() { process.exit(1); }") (function() { process.exit(1); })() }',
967
- stack: [],
968
- };
969
- const stringSinkContextPositiveV2 = {
970
- name: 'vm.Script',
971
- value: 'function wrapperFunction() { function() { process.exit(1); } }',
972
- stack: [],
973
- };
974
- const stringSinkContextNegative = {
975
- name: 'vm.Script',
976
- value: 'function() { console.log("function() { process.exit(1); }") }',
977
- stack: [],
978
- };
979
- const objectSinkContextPositive = {
980
- name: 'vm.Script.runInNewContext',
981
- value: {
982
- numValue: 1,
983
- strValue: 'Contrast',
984
- vulnerableValue: 'function() { process.exit(1); }',
985
- anotherStr: 'Security',
986
- booleanValue: true
987
- },
988
- stack: [],
989
- };
990
- const objectSinkContextNegative = {
991
- name: 'vm.Script.runInNewContext',
992
- value: {
993
- numValue: 1,
994
- strValue: '',
995
- booleanValue: true
996
- },
997
- stack: [],
998
- };
999
- const findings = {
1000
- startIndex: 61,
1001
- endIndex: 92,
1002
- boundaryIndex: 61,
1003
- codeString: 'function() { process.exit(1); }'
1004
- };
1005
-
1006
- const objectFindings = {
1007
- startIndex: 0,
1008
- endIndex: 30,
1009
- boundaryIndex: 0,
1010
- codeString: 'function() { process.exit(1); }'
1011
- };
1012
-
1013
- [
1014
- {
1015
- blocks: false,
1016
- type: 'codeString',
1017
- findings,
1018
- sourceContext: makeSourceContext(
1019
- [
1020
- {
1021
- ruleId: 'ssjs-injection',
1022
- value: 'function() { process.exit(1); }',
1023
- exploitMetadata: [],
1024
- }
1025
- ]
1026
- ),
1027
- expectedDetails: [
1028
- {
1029
- sinkContext: stringSinkContextPositive,
1030
- findings,
1031
- },
1032
- {
1033
- sinkContext: stringSinkContextPositiveV2,
1034
- findings: {
1035
- startIndex: 29,
1036
- boundaryIndex: 29,
1037
- endIndex: 60,
1038
- codeString: 'function() { process.exit(1); }'
1039
- },
1040
- }
1041
- ]
1042
- },
1043
- {
1044
- blocks: true,
1045
- type: 'codeString',
1046
- findings,
1047
- sourceContext: makeSourceContext(
1048
- [
1049
- {
1050
- ruleId: 'ssjs-injection',
1051
- value: 'function() { process.exit(1); }',
1052
- exploitMetadata: [],
1053
- }
1054
- ],
1055
- {
1056
- ['ssjs-injection']: 'block'
1057
- }
1058
- ),
1059
- expectedDetails: [
1060
- {
1061
- sinkContext: stringSinkContextPositive,
1062
- findings,
1063
- },
1064
- ]
1065
- },
1066
- {
1067
- blocks: false,
1068
- type: 'codeString',
1069
- findings,
1070
- sourceContext: makeSourceContext(
1071
- [
1072
- {
1073
- ruleId: 'ssjs-injection',
1074
- value: 'function() { process.exit(1); }',
1075
- exploitMetadata: [],
1076
- }
1077
- ],
1078
- {
1079
- ['ssjs-injection']: 'off'
1080
- }
1081
- ),
1082
- expectedDetails: []
1083
- },
1084
- {
1085
- blocks: false,
1086
- type: 'contextObject',
1087
- objectFindings,
1088
- sourceContext: makeSourceContext(
1089
- [
1090
- {
1091
- ruleId: 'ssjs-injection',
1092
- value: 'function() { process.exit(1); }',
1093
- exploitMetadata: [],
1094
- }
1095
- ]
1096
- ),
1097
- expectedDetails: [
1098
- {
1099
- sinkContext: objectSinkContextPositive,
1100
- findings: objectFindings,
1101
- },
1102
- {
1103
- sinkContext: objectSinkContextPositive,
1104
- findings: objectFindings,
1105
- }
1106
- ]
1107
- },
1108
- {
1109
- blocks: true,
1110
- type: 'contextObject',
1111
- objectFindings,
1112
- sourceContext: makeSourceContext(
1113
- [
1114
- {
1115
- ruleId: 'ssjs-injection',
1116
- value: 'function() { process.exit(1); }',
1117
- exploitMetadata: [],
1118
- }
1119
- ],
1120
- {
1121
- ['ssjs-injection']: 'block'
1122
- }
1123
- ),
1124
- expectedDetails: [
1125
- {
1126
- sinkContext: objectSinkContextPositive,
1127
- findings: objectFindings,
1128
- },
1129
- ]
1130
- },
1131
- {
1132
- blocks: false,
1133
- type: 'contextObject',
1134
- objectFindings,
1135
- sourceContext: makeSourceContext(
1136
- [
1137
- {
1138
- ruleId: 'ssjs-injection',
1139
- value: 'function() { process.exit(1); }',
1140
- exploitMetadata: [],
1141
- }
1142
- ],
1143
- {
1144
- ['ssjs-injection']: 'off'
1145
- }
1146
- ),
1147
- expectedDetails: []
1148
- }
1149
- ].forEach(({ blocks, type, sourceContext, expectedDetails }) => {
1150
- it(makeTestDescription(blocks), function () {
1151
- const testFn = () => {
1152
- if (type == 'codeString') {
1153
- handlers.ssjsInjection(sourceContext, stringSinkContextPositive);
1154
- handlers.ssjsInjection(sourceContext, stringSinkContextPositiveV2);
1155
- handlers.ssjsInjection(sourceContext, stringSinkContextNegative);
1156
- } else {
1157
- handlers.ssjsInjection(sourceContext, objectSinkContextPositive);
1158
- handlers.ssjsInjection(sourceContext, objectSinkContextPositive);
1159
- handlers.ssjsInjection(sourceContext, objectSinkContextNegative);
1160
- }
1161
- };
1162
-
1163
- const test = expect(testFn);
1164
- blocks ? test.to.throw('SecurityException') : test.not.to.throw();
1165
-
1166
- const ssjsiResults = sourceContext.resultsMap['ssjs-injection'];
1167
- expect(ssjsiResults[0].exploitMetadata).to.deep.equal(expectedDetails);
1168
- });
1169
- });
1170
- });
1171
-
1172
- describe('handleReflectedXss()', function () {
1173
- const ruleId = 'reflected-xss';
1174
- const value = '" onmouseover="alert(3)"';
1175
- const sinkContextPositive = {
1176
- value,
1177
- stack: []
1178
- };
1179
- const sinkContextNegative = {
1180
- value: 'foo',
1181
- stack: []
1182
- };
1183
- const findings = {
1184
- value
1185
- };
1186
-
1187
- [
1188
- {
1189
- blocks: false,
1190
- sourceContext: makeSourceContext(
1191
- [{
1192
- ruleId,
1193
- value,
1194
- exploitMetadata: []
1195
- }]
1196
- ),
1197
- expectedDetails: [
1198
- {
1199
- sinkContext: sinkContextPositive,
1200
- findings
1201
- },
1202
- {
1203
- sinkContext: sinkContextPositive,
1204
- findings
1205
- }
1206
- ]
1207
- },
1208
- {
1209
- blocks: false,
1210
- sourceContext: makeSourceContext(
1211
- [{
1212
- ruleId,
1213
- value,
1214
- exploitMetadata: [],
1215
- }],
1216
- {
1217
- [ruleId]: 'off'
1218
- }
1219
- ),
1220
- expectedDetails: []
1221
- }
1222
- ].forEach(({ blocks, sourceContext, expectedDetails }) => {
1223
- it(makeTestDescription(blocks), function () {
1224
- const testFn = () => {
1225
- handlers.handleReflectedXss(sourceContext, sinkContextPositive);
1226
- handlers.handleReflectedXss(sourceContext, sinkContextPositive);
1227
- handlers.handleReflectedXss(sourceContext, sinkContextNegative);
1228
- };
1229
-
1230
- const test = expect(testFn);
1231
- blocks ? test.to.throw('SecurityException') : test.not.to.throw();
1232
-
1233
- expect(sourceContext.resultsMap['reflected-xss'][0].exploitMetadata).to.deep.equal(expectedDetails);
1234
- });
1235
- });
1236
- });
1237
- });