@contrast/protect 1.66.0 → 1.68.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.
@@ -18,6 +18,7 @@
18
18
  const {
19
19
  BLOCKING_MODES,
20
20
  isString,
21
+ InputType,
21
22
  Rule: { UNTRUSTED_DESERIALIZATION }
22
23
  } = require('@contrast/common');
23
24
 
@@ -25,6 +26,7 @@ const NODE_SERIALIZE_RCE_TOKEN = '_$$ND_FUNC$$_';
25
26
 
26
27
  module.exports = function(core) {
27
28
  const {
29
+ protect,
28
30
  protect: {
29
31
  hardening,
30
32
  throwSecurityException,
@@ -40,32 +42,46 @@ module.exports = function(core) {
40
42
  return results;
41
43
  }
42
44
 
43
- hardening.handleUntrustedDeserialization = function(sourceContext, sinkContext) {
44
- const ruleId = UNTRUSTED_DESERIALIZATION;
45
- const mode = sourceContext.policy[ruleId];
46
- const { name, value, stacktraceOpts } = sinkContext;
45
+ function handleFindings(sourceContext, sinkContext, ruleId, result, findings, mode) {
46
+ const { stacktraceOpts } = sinkContext;
47
47
 
48
- if (mode === 'off') return;
48
+ captureStacktrace(sinkContext, stacktraceOpts);
49
+ getResults(sourceContext, ruleId).push(result);
49
50
 
50
- if (name === 'node-serialize.unserialize') {
51
- if (!isString(value) || !value.indexOf(NODE_SERIALIZE_RCE_TOKEN)) return;
51
+ let blockInfo;
52
+ if (BLOCKING_MODES.includes(mode)) {
53
+ result.blocked = true;
54
+ blockInfo = [mode, ruleId];
55
+ sourceContext.securityException = blockInfo;
56
+ }
52
57
 
53
- const blocked = BLOCKING_MODES.includes(mode);
54
- const results = getResults(sourceContext, ruleId);
58
+ protect.reportFinding({ findings, result, sinkContext });
55
59
 
56
- captureStacktrace(sinkContext, stacktraceOpts);
57
- results.push({
58
- value: sinkContext.value,
59
- blocked,
60
- exploitMetadata: [{ deserializer: name, command: false }],
61
- sinkContext,
62
- });
60
+ if (blockInfo) throwSecurityException(sourceContext);
61
+ }
63
62
 
64
- if (blocked) {
65
- sourceContext.securityException = [mode, ruleId];
66
- throwSecurityException(sourceContext);
67
- }
68
- }
63
+ hardening.handleUntrustedDeserialization = function (sourceContext, sinkContext) {
64
+ const ruleId = UNTRUSTED_DESERIALIZATION;
65
+ const mode = sourceContext.policy.getRuleMode(ruleId);
66
+ const { name, value } = sinkContext;
67
+
68
+ if (
69
+ mode === 'off' ||
70
+ name !== 'node-serialize.unserialize' ||
71
+ !isString(value) ||
72
+ !value.indexOf(NODE_SERIALIZE_RCE_TOKEN)
73
+ ) return;
74
+
75
+ const result = {
76
+ value: sinkContext.value,
77
+ ruleId: UNTRUSTED_DESERIALIZATION,
78
+ blocked: false,
79
+ exploited: true,
80
+ inputType: InputType.UNKNOWN,
81
+ };
82
+ const findings = { deserializer: name, command: false };
83
+
84
+ handleFindings(sourceContext, sinkContext, ruleId, result, findings, mode);
69
85
  };
70
86
 
71
87
  return hardening;
package/lib/index.d.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * way not consistent with the End User License Agreement.
14
14
  */
15
15
 
16
- import { ReqData, ProtectMessage, ResultMap, ProtectRuleMode, Blocker } from '@contrast/common';
16
+ import { ReqData, ProtectMessage, ResultMap, ProtectRuleMode, Blocker, ProtectFindingEventArg } from '@contrast/common';
17
17
  import { IncomingMessage, ServerResponse } from 'node:http';
18
18
  import * as http from 'node:http';
19
19
  import * as https from 'node:https';
@@ -60,7 +60,8 @@ export interface Protect {
60
60
  makeSourceContext: (req: IncomingMessage, res: ServerResponse) => ProtectRequestStore,
61
61
  throwSecurityException: (sourceContext: ProtectRequestStore) => void,
62
62
  policy: ProtectPolicy,
63
- getPolicy(): ProtectPolicy, // creates copy for request scope
63
+ getPolicy: () => ProtectPolicy, // creates copy for request scope
64
+ reportFindings: (e: ProtectFindingEventArg) => void,
64
65
  inputAnalysis: {
65
66
  handleConnect: (sourceContext: ProtectRequestStore, connectInputs: ConnectInputs) => undefined | [string, string],
66
67
  handleRequestEnd: (sourceContext: ProtectRequestStore) => void, //NYI
package/lib/index.js CHANGED
@@ -16,7 +16,7 @@
16
16
  'use strict';
17
17
 
18
18
  const { isMainThread } = require('node:worker_threads');
19
- const { callChildComponentMethodsSync } = require('@contrast/common');
19
+ const { callChildComponentMethodsSync, Event } = require('@contrast/common');
20
20
  const { ConfigSource } = require('@contrast/config');
21
21
 
22
22
  module.exports = function(core) {
@@ -37,7 +37,7 @@ module.exports = function(core) {
37
37
  require('./hardening')(core);
38
38
  require('./semantic-analysis')(core);
39
39
 
40
- protect.install = function() {
40
+ protect.install = function install() {
41
41
  // only force instrumentation if assess is explicitly enabled in local config
42
42
  const forceInstrumentation =
43
43
  config.preinstrument &&
@@ -60,6 +60,13 @@ module.exports = function(core) {
60
60
  ctx.store.protect = protect.makeSourceContext(ctx);
61
61
  });
62
62
 
63
+ protect.reportFinding = function reportFinding(data) {
64
+ core.messages.emit(Event.PROTECT_FINDING, {
65
+ store: core.scopes.sources.getStore(),
66
+ ...data,
67
+ });
68
+ };
69
+
63
70
  return protect;
64
71
  };
65
72