@contrast/protect 1.53.1 → 1.54.1

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 +15 -12
  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 -1236
  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,1236 +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
- value: 'select * from foo where col = """ and 1 = 1; --" or col = "" and 1 = 1; --',
239
- stack: [],
240
- };
241
- const sinkContextNegative = {
242
- name: 'mysql.query',
243
- value: 'select * from foo where col = """ and 1 = 1; --"',
244
- stack: [],
245
- };
246
- const findings = {
247
- boundaryIndex: 59,
248
- endIndex: 74,
249
- overrunIndex: 61,
250
- startIndex: 59,
251
- };
252
-
253
- [
254
- {
255
- blocks: false,
256
- findings,
257
- sourceContext: makeSourceContext(
258
- [
259
- {
260
- ruleId: 'sql-injection',
261
- value: '" and 1 = 1; --',
262
- exploitMetadata: [],
263
- }
264
- ]
265
- ),
266
- expectedDetails: [
267
- {
268
- sinkContext: sinkContextPositive,
269
- findings,
270
- },
271
- {
272
- sinkContext: sinkContextPositive,
273
- findings,
274
- }
275
- ]
276
- },
277
- {
278
- blocks: true,
279
- findings: {
280
- startIndex: 0,
281
- endIndex: 30,
282
- overrunIndex: 0,
283
- boundaryIndex: 0,
284
- },
285
- sourceContext: makeSourceContext(
286
- [
287
- {
288
- ruleId: 'sql-injection',
289
- // this is the exact value of the query - this is to get coverage
290
- value: 'select * from foo where col = """ and 1 = 1; --" or col = "" and 1 = 1; --',
291
- exploitMetadata: [],
292
- }
293
- ],
294
- {
295
- ['sql-injection']: 'block'
296
- }
297
- ),
298
- expectedDetails: [
299
- {
300
- sinkContext: sinkContextPositive,
301
- findings: {
302
- startIndex: 0,
303
- endIndex: 73,
304
- overrunIndex: 0,
305
- boundaryIndex: 0,
306
- },
307
- },
308
- ]
309
- },
310
- {
311
- blocks: false,
312
- findings: {
313
- startIndex: 0,
314
- endIndex: 30,
315
- overrunIndex: 0,
316
- boundaryIndex: 0,
317
- },
318
- sourceContext: makeSourceContext(
319
- [
320
- {
321
- ruleId: 'sql-injection',
322
- value: '" and 1 = 1; --',
323
- exploitMetadata: [],
324
- }
325
- ],
326
- {
327
- ['sql-injection']: 'off'
328
- }
329
- ),
330
- expectedDetails: []
331
- }
332
- ].forEach(({ blocks, sourceContext, expectedDetails }) => {
333
- it(makeTestDescription(blocks), function () {
334
- const testFn = () => {
335
- handlers.handleSqlInjection(sourceContext, sinkContextPositive);
336
- handlers.handleSqlInjection(sourceContext, sinkContextPositive);
337
- handlers.handleSqlInjection(sourceContext, sinkContextNegative);
338
- };
339
-
340
- const test = expect(testFn);
341
- blocks ? test.to.throw('SecurityException') : test.not.to.throw();
342
-
343
- const sqliResults = sourceContext.resultsMap['sql-injection'];
344
- expect(sqliResults[0].exploitMetadata).to.deep.equal(expectedDetails);
345
- });
346
- });
347
- });
348
-
349
- describe('nosqlInjectionMongo()', function () {
350
- const expansionSinkContextPositive = {
351
- name: 'mongodb.find',
352
- value: { id: { '$ne': 0 }, name: 'testValue' },
353
- stack: [],
354
- };
355
- const expansionSinkContextNegative = {
356
- name: 'mongodb.find',
357
- value: { id: 1 },
358
- stack: [],
359
- };
360
- const expansionSinkContextWrong = {
361
- name: 'mongodb.find',
362
- value: '{ id: { "$ne": 0 }, name: "testValue" }',
363
- stack: [],
364
- };
365
- const expansionFindings = {
366
- start: 8,
367
- end: 11,
368
- inputBoundaryIndex: 0,
369
- boundaryOverrunIndex: 8
370
- };
371
- const stringSinkContextPositive = {
372
- name: 'mongodb.Db.eval',
373
- value: 'function() { console.log(`function() { return "hi"; }`) (function() { return "hi"; })() }',
374
- stack: [],
375
- };
376
- const stringSinkContextNegative = {
377
- name: 'mongodb.Db.eval',
378
- value: 'function() { console.log(`function() { return "hi"; }`) }',
379
- stack: [],
380
- };
381
- const stringSinkContextWrong = {
382
- name: 'mongodb.Db.eval',
383
- value: { id: { '$ne': 0 }, name: 'testValue' },
384
- stack: [],
385
- };
386
- const stringFindings = {
387
- start: 58,
388
- end: 84,
389
- boundaryOverrunIndex: 0,
390
- inputBoundaryIndex: 0,
391
- };
392
-
393
- [
394
- {
395
- blocks: false,
396
- type: 'expansionInjection',
397
- findings: expansionFindings,
398
- sourceContext: makeSourceContext(
399
- [
400
- {
401
- ruleId: 'nosql-injection-mongo',
402
- value: { '$ne': 0 },
403
- key: '$ne',
404
- exploitMetadata: [],
405
- mongoContext: { inputToCheck: 0 },
406
- }
407
- ]
408
- ),
409
- expectedDetails: [
410
- {
411
- sinkContext: expansionSinkContextPositive,
412
- findings: expansionFindings,
413
- },
414
- {
415
- sinkContext: expansionSinkContextPositive,
416
- findings: expansionFindings,
417
- }
418
- ]
419
- },
420
- {
421
- blocks: true,
422
- type: 'expansionInjection',
423
- findings: expansionFindings,
424
- sourceContext: makeSourceContext(
425
- [
426
- {
427
- ruleId: 'nosql-injection-mongo',
428
- // this is the exact value of the query - this is to get coverage
429
- value: { '$ne': 0 },
430
- key: '$ne',
431
- exploitMetadata: [],
432
- mongoContext: { inputToCheck: 0 },
433
- }
434
- ],
435
- {
436
- ['nosql-injection-mongo']: 'block'
437
- }
438
- ),
439
- expectedDetails: [
440
- {
441
- sinkContext: expansionSinkContextPositive,
442
- findings: expansionFindings,
443
- },
444
- ]
445
- },
446
- {
447
- blocks: false,
448
- type: 'expansionInjection',
449
- findings: expansionFindings,
450
- sourceContext: makeSourceContext(
451
- [
452
- {
453
- ruleId: 'nosql-injection-mongo',
454
- value: { '$ne': 0 },
455
- key: '$ne',
456
- exploitMetadata: [],
457
- mongoContext: { inputToCheck: 0 },
458
- }
459
- ],
460
- {
461
- ['nosql-injection-mongo']: 'off'
462
- }
463
- ),
464
- expectedDetails: []
465
- },
466
- {
467
- blocks: false,
468
- type: 'stringInjection',
469
- stringFindings,
470
- sourceContext: makeSourceContext(
471
- [
472
- {
473
- ruleId: 'ssjs-injection',
474
- value: 'function() { return "hi"; }',
475
- exploitMetadata: [],
476
- }
477
- ]
478
- ),
479
- expectedDetails: [
480
- {
481
- sinkContext: stringSinkContextPositive,
482
- findings: stringFindings,
483
- },
484
- {
485
- sinkContext: stringSinkContextPositive,
486
- findings: stringFindings,
487
- }
488
- ]
489
- },
490
- {
491
- blocks: true,
492
- type: 'stringInjection',
493
- findings: stringFindings,
494
- sourceContext: makeSourceContext(
495
- [
496
- {
497
- ruleId: 'ssjs-injection',
498
- value: 'function() { return "hi"; }',
499
- exploitMetadata: [],
500
- }
501
- ],
502
- {
503
- ['nosql-injection-mongo']: 'block'
504
- }
505
- ),
506
- expectedDetails: [
507
- {
508
- sinkContext: stringSinkContextPositive,
509
- findings: stringFindings,
510
- },
511
- ]
512
- },
513
- {
514
- blocks: false,
515
- type: 'stringInjection',
516
- findings: stringFindings,
517
- sourceContext: makeSourceContext(
518
- [
519
- {
520
- ruleId: 'ssjs-injection',
521
- value: 'function() { return "hi"; }',
522
- exploitMetadata: [],
523
- }
524
- ],
525
- {
526
- ['ssjs-injection']: 'off'
527
- }
528
- ),
529
- expectedDetails: []
530
- },
531
- ].forEach(({ blocks, type, sourceContext, expectedDetails }) => {
532
- it(makeTestDescription(blocks), function () {
533
- const testFn = () => {
534
- if (type == 'expansionInjection') {
535
- handlers.nosqlInjectionMongo(sourceContext, expansionSinkContextPositive);
536
- handlers.nosqlInjectionMongo(sourceContext, expansionSinkContextPositive);
537
- handlers.nosqlInjectionMongo(sourceContext, expansionSinkContextNegative);
538
- handlers.nosqlInjectionMongo(sourceContext, expansionSinkContextWrong);
539
- } else {
540
- handlers.nosqlInjectionMongo(sourceContext, stringSinkContextPositive);
541
- handlers.nosqlInjectionMongo(sourceContext, stringSinkContextPositive);
542
- handlers.nosqlInjectionMongo(sourceContext, stringSinkContextNegative);
543
- handlers.nosqlInjectionMongo(sourceContext, stringSinkContextWrong);
544
- }
545
- };
546
-
547
- const test = expect(testFn);
548
- blocks ? test.to.throw('SecurityException') : test.not.to.throw();
549
-
550
- const nosqliResults = sourceContext.resultsMap['nosql-injection-mongo'];
551
- const ssjsResult = sourceContext.resultsMap['ssjs-injection'];
552
- if (type == 'expansionInjection') {
553
- expect(nosqliResults[0].exploitMetadata).to.deep.equal(expectedDetails);
554
- } else {
555
- expect(ssjsResult[0].exploitMetadata).to.deep.equal(expectedDetails);
556
- }
557
- });
558
- });
559
-
560
- [
561
- {
562
- userInput: 'function() ',
563
- cmd: 'function() { return true; }',
564
- start: 0,
565
- end: 10,
566
- },
567
- {
568
- userInput: '(function() { return true })()',
569
- cmd: 'function() { return (function() { return true })(); }',
570
- start: 20,
571
- end: 49,
572
- }
573
- ].forEach(({ userInput, cmd, start, end }, index) => {
574
- it(`sourceContext-injection as function execution - case ${index + 1}`, function () {
575
- const sinkContexts = [
576
- {
577
- value: { $where: cmd },
578
- name: 'mongodb.query'
579
- },
580
- {
581
- value: {
582
- $expr: {
583
- $function: {
584
- body: cmd,
585
- args: [],
586
- lang: 'js',
587
- }
588
- }
589
- },
590
- name: 'mongodb.query'
591
- },
592
- ];
593
-
594
- sinkContexts.forEach(sc => {
595
- const data = {
596
- sourceContext: makeSourceContext(
597
- [
598
- {
599
- ruleId: 'ssjs-injection',
600
- value: userInput,
601
- idsList: [],
602
- exploitMetadata: [],
603
- }
604
- ]
605
- ),
606
- };
607
-
608
- handlers.nosqlInjectionMongo(data.sourceContext, sc);
609
- expect(data.sourceContext.resultsMap['nosql-injection-mongo']).to.have.lengthOf(1);
610
- expect(data.sourceContext.resultsMap['ssjs-injection']).to.have.lengthOf(1);
611
- expect(data.sourceContext.resultsMap['nosql-injection-mongo'][0]).to.eql({
612
- ruleId: 'nosql-injection-mongo',
613
- value: userInput,
614
- idsList: [],
615
- exploitMetadata: [
616
- {
617
- sinkContext: sc,
618
- findings: {
619
- start,
620
- end,
621
- boundaryOverrunIndex: 0,
622
- inputBoundaryIndex: 0
623
- }
624
- }
625
- ],
626
- mappedId: 'nosql-injection-mongo'
627
- });
628
- });
629
- });
630
- });
631
-
632
- it('sourceContext-injection as function execution - $accumulator', function () {
633
- const sinkContext = {
634
- value: {
635
- $group: {
636
- _id: null,
637
- count: {
638
- $accumulator: {
639
- init: 'function() { return { count: 0 } }',
640
- accumulate: 'function(state) { return { count: state.count + 1 } }',
641
- merge: 'function(state1, state2) { return { count: state1.count + state2.count } }',
642
- finalize: 'function(state) { return state; }',
643
- lang: 'js',
644
- }
645
- }
646
- }
647
- },
648
- name: 'mongodb.query'
649
- };
650
-
651
- [
652
- {
653
- value: 'function() { return { count: 0 } }',
654
- findings: {
655
- boundaryOverrunIndex: 0,
656
- end: 33,
657
- inputBoundaryIndex: 0,
658
- start: 0,
659
- }
660
- },
661
- {
662
- value: 'function(state) { return { count: state.count + 1 } }',
663
- findings: {
664
- boundaryOverrunIndex: 0,
665
- end: 52,
666
- inputBoundaryIndex: 0,
667
- start: 0,
668
- }
669
- },
670
- {
671
- value: 'function(state1, state2) { return { count: state1.count + state2.count } }',
672
- findings: {
673
- boundaryOverrunIndex: 0,
674
- end: 73,
675
- inputBoundaryIndex: 0,
676
- start: 0,
677
- }
678
- },
679
- {
680
- value: 'function(state) { return state; }',
681
- findings: {
682
- boundaryOverrunIndex: 0,
683
- end: 32,
684
- inputBoundaryIndex: 0,
685
- start: 0,
686
- }
687
- },
688
- ].forEach((obj) => {
689
- [sinkContext, Object.assign({}, sinkContext, { value: [sinkContext.value] })]
690
- .forEach(sc => {
691
- const sourceContext = makeSourceContext(
692
- [
693
- {
694
- ruleId: 'ssjs-injection',
695
- value: obj.value,
696
- exploitMetadata: [],
697
- }
698
- ]
699
- );
700
-
701
- handlers.nosqlInjectionMongo(sourceContext, sc);
702
- expect(sourceContext.resultsMap['nosql-injection-mongo']).to.have.lengthOf(1);
703
- expect(sourceContext.resultsMap['ssjs-injection']).to.have.lengthOf(1);
704
- expect(sourceContext.resultsMap['nosql-injection-mongo'][0]).to.eql({
705
- ruleId: 'nosql-injection-mongo',
706
- value: obj.value,
707
- exploitMetadata: [
708
- {
709
- sinkContext: sc,
710
- findings: obj.findings,
711
- }
712
- ],
713
- mappedId: 'nosql-injection-mongo'
714
- });
715
- });
716
- });
717
- });
718
-
719
- it('sourceContext-injection as function execution - not an attack', function () {
720
- const data = {
721
- sourceContext: makeSourceContext(
722
- [
723
- {
724
- ruleId: 'ssjs-injection',
725
- value: ' ',
726
- idsList: [],
727
- exploitMetadata: [],
728
- }
729
- ]
730
- ),
731
- };
732
-
733
- const sinkContext = {
734
- value: { $where: 'function() { return true; } ' },
735
- };
736
-
737
- handlers.nosqlInjectionMongo(data.sourceContext, sinkContext);
738
- expect(data.sourceContext.resultsMap['nosql-injection-mongo']).to.be.undefined;
739
- expect(data.sourceContext.resultsMap['ssjs-injection']).to.have.lengthOf(1);
740
- });
741
-
742
- it(`nosql-injection-mongo - ${agentLibIDListTypes.TRUE_CLAUSE_1} - object`, function () {
743
- const vector = "' || true || '";
744
-
745
- const sinkContext = {
746
- name: 'mongodb.FindCursor',
747
- value: {
748
- $where: `this.orderId === '${vector}'`
749
- }
750
- };
751
-
752
- const expectedDetails = {
753
- start: 18,
754
- end: 31,
755
- boundaryOverrunIndex: 0,
756
- inputBoundaryIndex: 0,
757
- };
758
-
759
- const data = {
760
- blocks: true,
761
- type: 'expansionInjection',
762
- findings: expansionFindings,
763
- sourceContext: makeSourceContext(
764
- [
765
- {
766
- ruleId: 'nosql-injection-mongo',
767
- inputType: 'JsonValue',
768
- path: [
769
- 'input',
770
- ],
771
- key: 'input',
772
- value: '\' || true || \'',
773
- score: 10,
774
- idsList: [
775
- agentLibIDListTypes.TRUE_CLAUSE_1,
776
- ],
777
- mappedId: 'nosql-injection',
778
- blocked: false,
779
- exploitMetadata: [
780
- ],
781
- }
782
- ],
783
- {
784
- ['nosql-injection-mongo']: 'block'
785
- }
786
- ),
787
- expectedDetails: [
788
- {
789
- sinkContext: {
790
- name: 'mongodb.FindCursor',
791
- value: {
792
- $where: `this.orderId === '${vector}'`,
793
- }
794
- },
795
- findings: expansionFindings,
796
- },
797
- ]
798
- };
799
-
800
- const test = expect(() => handlers.nosqlInjectionMongo(data.sourceContext, sinkContext));
801
- test.to.throw('SecurityException');
802
-
803
- const nosqliResults = data.sourceContext.resultsMap['nosql-injection-mongo'];
804
- expect(nosqliResults[0].exploitMetadata).to.deep.equal([
805
- {
806
- sinkContext: {
807
- name: 'mongodb.FindCursor',
808
- value: {
809
- $where: `this.orderId === '${vector}'`,
810
- },
811
- stack: [],
812
- },
813
- findings: expectedDetails,
814
- },
815
- ]);
816
- });
817
-
818
- it(`nosql-injection-mongo - ${agentLibIDListTypes.MONGO_SLEEP} - object`, function () {
819
- const sinkContext = {
820
- name: 'mongodb.FindCursor',
821
- value: {
822
- $where: 'this.title ==sleep(500)',
823
- }
824
- };
825
-
826
- const expectedDetails = {
827
- start: 13,
828
- end: 22,
829
- boundaryOverrunIndex: 0,
830
- inputBoundaryIndex: 0,
831
- };
832
-
833
- const data = {
834
- blocks: true,
835
- type: 'expansionInjection',
836
- findings: expansionFindings,
837
- sourceContext: makeSourceContext(
838
- [
839
- {
840
- ruleId: 'nosql-injection-mongo',
841
- inputType: 'JsonValue',
842
- path: [
843
- 'input',
844
- ],
845
- key: 'input',
846
- value: 'sleep(500)',
847
- score: 10,
848
- idsList: [
849
- agentLibIDListTypes.MONGO_SLEEP,
850
- ],
851
- mappedId: 'nosql-injection',
852
- blocked: false,
853
- exploitMetadata: [
854
- ],
855
- }
856
- ],
857
- {
858
- ['nosql-injection-mongo']: 'block'
859
- }
860
- ),
861
- expectedDetails: [
862
- {
863
- sinkContext: {
864
- name: 'mongodb.FindCursor',
865
- value: {
866
- $where: 'this.title ==sleep(500)',
867
- }
868
- },
869
- findings: expansionFindings,
870
- },
871
- ]
872
- };
873
-
874
- const test = expect(() => handlers.nosqlInjectionMongo(data.sourceContext, sinkContext));
875
- test.to.throw('SecurityException');
876
-
877
- const nosqliResults = data.sourceContext.resultsMap['nosql-injection-mongo'];
878
- expect(nosqliResults[0].exploitMetadata).to.deep.equal([
879
- {
880
- sinkContext: {
881
- name: 'mongodb.FindCursor',
882
- value: {
883
- $where: 'this.title ==sleep(500)',
884
- },
885
- stack: [
886
- ],
887
- },
888
- findings: expectedDetails,
889
- },
890
- ]);
891
- });
892
-
893
- it(`nosql-injection-mongo - ${agentLibIDListTypes.MONGO_SLEEP} - string`, function () {
894
- const sinkContext = {
895
- name: 'mongodb.FindCursor',
896
- value: 'sleep(500)'
897
- };
898
-
899
- const expectedDetails = {
900
- start: 0,
901
- end: 9,
902
- boundaryOverrunIndex: 0,
903
- inputBoundaryIndex: 0,
904
- };
905
-
906
- const data = {
907
- blocks: true,
908
- type: 'expansionInjection',
909
- findings: expansionFindings,
910
- sourceContext: makeSourceContext(
911
- [
912
- {
913
- ruleId: 'nosql-injection-mongo',
914
- inputType: 'JsonValue',
915
- path: [
916
- 'input',
917
- ],
918
- key: 'input',
919
- value: 'sleep(500)',
920
- score: 10,
921
- idsList: [
922
- agentLibIDListTypes.MONGO_SLEEP,
923
- ],
924
- mappedId: 'nosql-injection',
925
- blocked: false,
926
- exploitMetadata: [
927
- ],
928
- }
929
- ],
930
- {
931
- ['nosql-injection-mongo']: 'block'
932
- }
933
- ),
934
- expectedDetails: [
935
- {
936
- sinkContext: {
937
- name: 'mongodb.FindCursor',
938
- value: 'sleep(500)'
939
- },
940
- findings: expansionFindings,
941
- },
942
- ]
943
- };
944
-
945
- const test = expect(() => handlers.nosqlInjectionMongo(data.sourceContext, sinkContext));
946
- test.to.throw('SecurityException');
947
-
948
- const nosqliResults = data.sourceContext.resultsMap['nosql-injection-mongo'];
949
- expect(nosqliResults[0].exploitMetadata).to.deep.equal([
950
- {
951
- sinkContext: {
952
- name: 'mongodb.FindCursor',
953
- value: 'sleep(500)',
954
- stack: [],
955
- },
956
- findings: expectedDetails,
957
- },
958
- ]);
959
- });
960
- });
961
-
962
- describe('ssjsInjection()', function () {
963
- const stringSinkContextPositive = {
964
- name: 'vm.Script',
965
- value: 'function() { console.log("function() { process.exit(1); }") (function() { process.exit(1); })() }',
966
- stack: [],
967
- };
968
- const stringSinkContextPositiveV2 = {
969
- name: 'vm.Script',
970
- value: 'function wrapperFunction() { function() { process.exit(1); } }',
971
- stack: [],
972
- };
973
- const stringSinkContextNegative = {
974
- name: 'vm.Script',
975
- value: 'function() { console.log("function() { process.exit(1); }") }',
976
- stack: [],
977
- };
978
- const objectSinkContextPositive = {
979
- name: 'vm.Script.runInNewContext',
980
- value: {
981
- numValue: 1,
982
- strValue: 'Contrast',
983
- vulnerableValue: 'function() { process.exit(1); }',
984
- anotherStr: 'Security',
985
- booleanValue: true
986
- },
987
- stack: [],
988
- };
989
- const objectSinkContextNegative = {
990
- name: 'vm.Script.runInNewContext',
991
- value: {
992
- numValue: 1,
993
- strValue: '',
994
- booleanValue: true
995
- },
996
- stack: [],
997
- };
998
- const findings = {
999
- startIndex: 61,
1000
- endIndex: 92,
1001
- boundaryIndex: 61,
1002
- codeString: 'function() { process.exit(1); }'
1003
- };
1004
-
1005
- const objectFindings = {
1006
- startIndex: 0,
1007
- endIndex: 30,
1008
- boundaryIndex: 0,
1009
- codeString: 'function() { process.exit(1); }'
1010
- };
1011
-
1012
- [
1013
- {
1014
- blocks: false,
1015
- type: 'codeString',
1016
- findings,
1017
- sourceContext: makeSourceContext(
1018
- [
1019
- {
1020
- ruleId: 'ssjs-injection',
1021
- value: 'function() { process.exit(1); }',
1022
- exploitMetadata: [],
1023
- }
1024
- ]
1025
- ),
1026
- expectedDetails: [
1027
- {
1028
- sinkContext: stringSinkContextPositive,
1029
- findings,
1030
- },
1031
- {
1032
- sinkContext: stringSinkContextPositiveV2,
1033
- findings: {
1034
- startIndex: 29,
1035
- boundaryIndex: 29,
1036
- endIndex: 60,
1037
- codeString: 'function() { process.exit(1); }'
1038
- },
1039
- }
1040
- ]
1041
- },
1042
- {
1043
- blocks: true,
1044
- type: 'codeString',
1045
- findings,
1046
- sourceContext: makeSourceContext(
1047
- [
1048
- {
1049
- ruleId: 'ssjs-injection',
1050
- value: 'function() { process.exit(1); }',
1051
- exploitMetadata: [],
1052
- }
1053
- ],
1054
- {
1055
- ['ssjs-injection']: 'block'
1056
- }
1057
- ),
1058
- expectedDetails: [
1059
- {
1060
- sinkContext: stringSinkContextPositive,
1061
- findings,
1062
- },
1063
- ]
1064
- },
1065
- {
1066
- blocks: false,
1067
- type: 'codeString',
1068
- findings,
1069
- sourceContext: makeSourceContext(
1070
- [
1071
- {
1072
- ruleId: 'ssjs-injection',
1073
- value: 'function() { process.exit(1); }',
1074
- exploitMetadata: [],
1075
- }
1076
- ],
1077
- {
1078
- ['ssjs-injection']: 'off'
1079
- }
1080
- ),
1081
- expectedDetails: []
1082
- },
1083
- {
1084
- blocks: false,
1085
- type: 'contextObject',
1086
- objectFindings,
1087
- sourceContext: makeSourceContext(
1088
- [
1089
- {
1090
- ruleId: 'ssjs-injection',
1091
- value: 'function() { process.exit(1); }',
1092
- exploitMetadata: [],
1093
- }
1094
- ]
1095
- ),
1096
- expectedDetails: [
1097
- {
1098
- sinkContext: objectSinkContextPositive,
1099
- findings: objectFindings,
1100
- },
1101
- {
1102
- sinkContext: objectSinkContextPositive,
1103
- findings: objectFindings,
1104
- }
1105
- ]
1106
- },
1107
- {
1108
- blocks: true,
1109
- type: 'contextObject',
1110
- objectFindings,
1111
- sourceContext: makeSourceContext(
1112
- [
1113
- {
1114
- ruleId: 'ssjs-injection',
1115
- value: 'function() { process.exit(1); }',
1116
- exploitMetadata: [],
1117
- }
1118
- ],
1119
- {
1120
- ['ssjs-injection']: 'block'
1121
- }
1122
- ),
1123
- expectedDetails: [
1124
- {
1125
- sinkContext: objectSinkContextPositive,
1126
- findings: objectFindings,
1127
- },
1128
- ]
1129
- },
1130
- {
1131
- blocks: false,
1132
- type: 'contextObject',
1133
- objectFindings,
1134
- sourceContext: makeSourceContext(
1135
- [
1136
- {
1137
- ruleId: 'ssjs-injection',
1138
- value: 'function() { process.exit(1); }',
1139
- exploitMetadata: [],
1140
- }
1141
- ],
1142
- {
1143
- ['ssjs-injection']: 'off'
1144
- }
1145
- ),
1146
- expectedDetails: []
1147
- }
1148
- ].forEach(({ blocks, type, sourceContext, expectedDetails }) => {
1149
- it(makeTestDescription(blocks), function () {
1150
- const testFn = () => {
1151
- if (type == 'codeString') {
1152
- handlers.ssjsInjection(sourceContext, stringSinkContextPositive);
1153
- handlers.ssjsInjection(sourceContext, stringSinkContextPositiveV2);
1154
- handlers.ssjsInjection(sourceContext, stringSinkContextNegative);
1155
- } else {
1156
- handlers.ssjsInjection(sourceContext, objectSinkContextPositive);
1157
- handlers.ssjsInjection(sourceContext, objectSinkContextPositive);
1158
- handlers.ssjsInjection(sourceContext, objectSinkContextNegative);
1159
- }
1160
- };
1161
-
1162
- const test = expect(testFn);
1163
- blocks ? test.to.throw('SecurityException') : test.not.to.throw();
1164
-
1165
- const ssjsiResults = sourceContext.resultsMap['ssjs-injection'];
1166
- expect(ssjsiResults[0].exploitMetadata).to.deep.equal(expectedDetails);
1167
- });
1168
- });
1169
- });
1170
-
1171
- describe('handleReflectedXss()', function () {
1172
- const ruleId = 'reflected-xss';
1173
- const value = '" onmouseover="alert(3)"';
1174
- const sinkContextPositive = {
1175
- value,
1176
- stack: []
1177
- };
1178
- const sinkContextNegative = {
1179
- value: 'foo',
1180
- stack: []
1181
- };
1182
- const findings = {
1183
- value
1184
- };
1185
-
1186
- [
1187
- {
1188
- blocks: false,
1189
- sourceContext: makeSourceContext(
1190
- [{
1191
- ruleId,
1192
- value,
1193
- exploitMetadata: []
1194
- }]
1195
- ),
1196
- expectedDetails: [
1197
- {
1198
- sinkContext: sinkContextPositive,
1199
- findings
1200
- },
1201
- {
1202
- sinkContext: sinkContextPositive,
1203
- findings
1204
- }
1205
- ]
1206
- },
1207
- {
1208
- blocks: false,
1209
- sourceContext: makeSourceContext(
1210
- [{
1211
- ruleId,
1212
- value,
1213
- exploitMetadata: [],
1214
- }],
1215
- {
1216
- [ruleId]: 'off'
1217
- }
1218
- ),
1219
- expectedDetails: []
1220
- }
1221
- ].forEach(({ blocks, sourceContext, expectedDetails }) => {
1222
- it(makeTestDescription(blocks), function () {
1223
- const testFn = () => {
1224
- handlers.handleReflectedXss(sourceContext, sinkContextPositive);
1225
- handlers.handleReflectedXss(sourceContext, sinkContextPositive);
1226
- handlers.handleReflectedXss(sourceContext, sinkContextNegative);
1227
- };
1228
-
1229
- const test = expect(testFn);
1230
- blocks ? test.to.throw('SecurityException') : test.not.to.throw();
1231
-
1232
- expect(sourceContext.resultsMap['reflected-xss'][0].exploitMetadata).to.deep.equal(expectedDetails);
1233
- });
1234
- });
1235
- });
1236
- });