@contrast/assess 1.36.0 → 1.37.0

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.
@@ -15,6 +15,7 @@
15
15
 
16
16
  'use strict';
17
17
 
18
+ const semver = require('semver');
18
19
  const { DataflowTag, primordials: { StringPrototypeSubstring } } = require('@contrast/common');
19
20
  const { patchType } = require('../../common');
20
21
  const { userDefinedType } = require('./common');
@@ -34,6 +35,99 @@ module.exports = function (core) {
34
35
  },
35
36
  } = core;
36
37
 
38
+ function patchCastForQuery(SchemaString, { version }) {
39
+ if (semver.gte(version, '7.0.0')) {
40
+ patcher.patch(SchemaString.prototype, 'castForQuery', {
41
+ name: 'mongoose.SchemaString.prototype.castForQuery',
42
+ patchType,
43
+ post(data) {
44
+ // if a conditional is provided as the first argument castForQuery gets
45
+ // called subsequently with the individual value(s)
46
+ if (!getSourceContext() || data.args[0]) return;
47
+
48
+ const strInfo = tracker.getData(data.result);
49
+ if (!strInfo) return;
50
+
51
+ const event = createPropagationEvent({
52
+ addedTags: [DataflowTag.STRING_TYPE_CHECKED],
53
+ name: data.name,
54
+ moduleName: 'mongoose',
55
+ methodName: 'SchemaString.prototype.castForQuery',
56
+ history: [{ ...strInfo }],
57
+ object: {
58
+ tracked: false,
59
+ value: 'mongoose.SchemaString',
60
+ },
61
+ args: [{ tracked: true, value: strInfo.value }],
62
+ result: { tracked: true, value: data.result },
63
+ source: 'P1',
64
+ tags: {
65
+ ...strInfo.tags,
66
+ [DataflowTag.STRING_TYPE_CHECKED]: [0, data.result.length - 1],
67
+ },
68
+ target: 'R',
69
+ stacktraceOpts: {
70
+ prependFrames: [data.orig],
71
+ },
72
+ });
73
+
74
+ if (!event) return;
75
+
76
+ // in case the event type changed e.g. Source->Propagation
77
+ for (const key of Object.keys(strInfo)) {
78
+ if (key === 'value' || key === 'extern') continue;
79
+ delete strInfo[key];
80
+ }
81
+
82
+ Object.assign(strInfo, event);
83
+ }
84
+ });
85
+ } else {
86
+ // v6
87
+ patcher.patch(SchemaString.prototype, '_castForQuery', {
88
+ name: 'mongoose.SchemaString.prototype._castForQuery',
89
+ patchType,
90
+ post(data) {
91
+ const strInfo = tracker.getData(data.result);
92
+ if (!strInfo) return;
93
+
94
+ const event = createPropagationEvent({
95
+ addedTags: [DataflowTag.STRING_TYPE_CHECKED],
96
+ name: data.name,
97
+ moduleName: 'mongoose',
98
+ methodName: 'SchemaString.prototype._castForQuery',
99
+ history: [{ ...strInfo }],
100
+ object: {
101
+ tracked: false,
102
+ value: 'mongoose.SchemaString',
103
+ },
104
+ args: [{ tracked: true, value: strInfo.value }],
105
+ result: { tracked: true, value: data.result },
106
+ source: 'P0',
107
+ tags: {
108
+ ...strInfo.tags,
109
+ [DataflowTag.STRING_TYPE_CHECKED]: [0, data.result.length - 1],
110
+ },
111
+ target: 'R',
112
+ stacktraceOpts: {
113
+ prependFrames: [data.orig],
114
+ },
115
+ });
116
+
117
+ if (!event) return;
118
+
119
+ // in case the event type changed e.g. Source->Propagation
120
+ for (const key of Object.keys(strInfo)) {
121
+ if (key === 'value' || key === 'extern') continue;
122
+ delete strInfo[key];
123
+ }
124
+
125
+ Object.assign(strInfo, event);
126
+ }
127
+ });
128
+ }
129
+ }
130
+
37
131
  function patchEnum(SchemaString) {
38
132
  patcher.patch(SchemaString.prototype, 'enum', {
39
133
  name: 'mongoose.SchemaString.prototype.enum',
@@ -196,7 +290,8 @@ module.exports = function (core) {
196
290
  file: 'lib/schema/string.js',
197
291
  version: '>=6.0.0',
198
292
  },
199
- (SchemaString) => {
293
+ (SchemaString, metadata) => {
294
+ patchCastForQuery(SchemaString, metadata);
200
295
  patchEnum(SchemaString);
201
296
  patchDoValidate(SchemaString);
202
297
  patchDoValidateSync(SchemaString);
@@ -4,7 +4,7 @@ const { expect } = require('chai');
4
4
  const sinon = require('sinon');
5
5
  const { initAssessFixture } = require('@contrast/test/fixtures');
6
6
 
7
- describe('assess dataflow propagation mongoose.StringSchema', function () {
7
+ describe('assess dataflow propagation mongoose.SchemaString', function () {
8
8
  let core,
9
9
  trackString,
10
10
  simulateRequestScope,
@@ -28,6 +28,9 @@ describe('assess dataflow propagation mongoose.StringSchema', function () {
28
28
  validator: enumStub,
29
29
  }];
30
30
  };
31
+ MockSchemaString.prototype.castForQuery = function($c, v) {
32
+ return v;
33
+ };
31
34
  MockSchemaString.prototype.enum = function () {
32
35
  return this;
33
36
  };
@@ -39,7 +42,7 @@ describe('assess dataflow propagation mongoose.StringSchema', function () {
39
42
 
40
43
  tracker = core.assess.dataflow.tracker;
41
44
  core.assess.dataflow.propagation.mongooseInstrumentation.schemaString.install();
42
- core.depHooks.resolve.yield(MockSchemaString);
45
+ core.depHooks.resolve.yield(MockSchemaString, { version: '7.5.3' });
43
46
  });
44
47
 
45
48
  afterEach(function () {
@@ -60,6 +63,42 @@ describe('assess dataflow propagation mongoose.StringSchema', function () {
60
63
  });
61
64
  });
62
65
 
66
+ describe('SchemaString#castForQuery()', function () {
67
+ it('adds STRING_TYPE_CHECKED when called directly on a value', function () {
68
+ simulateRequestScope(function () {
69
+ const str = trackString('foo');
70
+ const strInfo = tracker.getData(str);
71
+
72
+ mockSchemaString.castForQuery(null, str);
73
+
74
+ expect(strInfo).to.deep.include({
75
+ value: 'foo',
76
+ addedTags: ['STRING_TYPE_CHECKED'],
77
+ tags: { UNTRUSTED: [0, 2], STRING_TYPE_CHECKED: [0, 2] },
78
+ });
79
+ });
80
+ });
81
+
82
+ it('does not add STRING_TYPE_CHECKED when called with a conditional', function () {
83
+ simulateRequestScope(function () {
84
+ const str = trackString('foo');
85
+ const strInfo = tracker.getData(str);
86
+
87
+ mockSchemaString.castForQuery('$ne', str);
88
+
89
+ expect(strInfo).to.deep.include({
90
+ value: 'foo',
91
+ tags: { UNTRUSTED: [0, 2] },
92
+ });
93
+ expect(strInfo).not.to.deep.include({
94
+ addedTags: ['STRING_TYPE_CHECKED'],
95
+ tags: { UNTRUSTED: [0, 2], STRING_TYPE_CHECKED: [0, 2] },
96
+ });
97
+
98
+ });
99
+ });
100
+ });
101
+
63
102
  it('SchemaString.doValidate adds STRING_TYPE_CHECKED tag', function () {
64
103
  return new Promise((resolve) => {
65
104
  simulateRequestScope(function () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/assess",
3
- "version": "1.36.0",
3
+ "version": "1.37.0",
4
4
  "description": "Contrast service providing framework-agnostic Assess support",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
@@ -19,14 +19,14 @@
19
19
  "dependencies": {
20
20
  "@contrast/common": "1.26.0",
21
21
  "@contrast/config": "1.34.0",
22
- "@contrast/core": "1.38.0",
23
- "@contrast/dep-hooks": "1.6.0",
22
+ "@contrast/core": "1.39.0",
23
+ "@contrast/dep-hooks": "1.7.0",
24
24
  "@contrast/distringuish": "^5.1.0",
25
- "@contrast/instrumentation": "1.16.0",
26
- "@contrast/logger": "1.11.0",
27
- "@contrast/patcher": "1.10.0",
28
- "@contrast/rewriter": "1.14.0",
29
- "@contrast/scopes": "1.7.0",
25
+ "@contrast/instrumentation": "1.17.0",
26
+ "@contrast/logger": "1.12.0",
27
+ "@contrast/patcher": "1.11.0",
28
+ "@contrast/rewriter": "1.15.0",
29
+ "@contrast/scopes": "1.8.0",
30
30
  "semver": "^7.6.0"
31
31
  }
32
32
  }