@contrast/protect
Advanced tools
Comparing version 1.8.1 to 1.9.0
@@ -47,3 +47,3 @@ /* | ||
if (isSecurityException && sourceContext) { | ||
const blockInfo = sourceContext.findings.securityException; | ||
const blockInfo = sourceContext.securityException; | ||
@@ -74,3 +74,3 @@ sourceContext.block(...blockInfo); | ||
if (isSecurityException && sourceContext) { | ||
const blockInfo = sourceContext.findings.securityException; | ||
const blockInfo = sourceContext.securityException; | ||
@@ -128,3 +128,3 @@ sourceContext.block(...blockInfo); | ||
if (isSecurityException && sourceContext) { | ||
const blockInfo = sourceContext.findings.securityException; | ||
const blockInfo = sourceContext.securityException; | ||
@@ -131,0 +131,0 @@ sourceContext.block(...blockInfo); |
@@ -65,3 +65,3 @@ /* | ||
} else { | ||
const blockInfo = sourceContext.findings.securityException; | ||
const blockInfo = sourceContext.securityException; | ||
sourceContext.block(...blockInfo); | ||
@@ -68,0 +68,0 @@ } |
@@ -42,3 +42,3 @@ /* | ||
if (isSecurityException && sourceContext && err.output.statusCode !== 403) { | ||
const [mode, ruleId] = sourceContext.findings.securityException; | ||
const [mode, ruleId] = sourceContext.securityException; | ||
@@ -45,0 +45,0 @@ err.output.statusCode = 403; |
@@ -49,3 +49,3 @@ /* | ||
data.obj.body = ''; | ||
const blockInfo = sourceContext.findings.securityException; | ||
const blockInfo = sourceContext.securityException; | ||
sourceContext.block(...blockInfo); | ||
@@ -52,0 +52,0 @@ return; |
@@ -32,5 +32,5 @@ /* | ||
function getResults(sourceContext, ruleId) { | ||
let results = sourceContext.findings.hardeningResultsMap[ruleId]; | ||
let results = sourceContext.resultsMap[ruleId]; | ||
if (!results) { | ||
results = sourceContext.findings.hardeningResultsMap[ruleId] = []; | ||
results = sourceContext.resultsMap[ruleId] = []; | ||
} | ||
@@ -55,4 +55,5 @@ return results; | ||
results.push({ | ||
value: sinkContext.value, | ||
blocked, | ||
findings: { deserializer: name, command: false }, | ||
exploitMetadata: [{ deserializer: name, command: false }], | ||
sinkContext, | ||
@@ -62,3 +63,3 @@ }); | ||
if (blocked) { | ||
sourceContext.findings.securityException = [mode, ruleId]; | ||
sourceContext.securityException = [mode, ruleId]; | ||
throwSecurityException(sourceContext); | ||
@@ -65,0 +66,0 @@ } |
@@ -19,3 +19,3 @@ /* | ||
import RequireHook from '@contrast/require-hook'; | ||
import { RulesConfig, Messages, ReqData, ProtectMessage, Findings } from '@contrast/common'; | ||
import { RulesConfig, Messages, ReqData, ProtectMessage, ResultMap, ProtectRuleMode } from '@contrast/common'; | ||
import { IncomingMessage, ServerResponse } from 'node:http'; | ||
@@ -62,3 +62,6 @@ import { Config } from '@contrast/config'; | ||
virtualPatches: any[]; // TODO | ||
findings: Findings; | ||
trackRequest: boolean; | ||
securityException?: [mode: ProtectRuleMode, ruleId: string]; | ||
bodyType?: 'json' | 'urlencoded'; | ||
resultsMap: Partial<ResultMap> | ||
} | ||
@@ -65,0 +68,0 @@ |
@@ -19,3 +19,3 @@ /* | ||
const agentLib = require('@contrast/agent-lib'); | ||
const { installChildComponentsSync } = require('@contrast/common'); | ||
const { callChildComponentMethodsSync } = require('@contrast/common'); | ||
@@ -39,3 +39,3 @@ module.exports = function(core) { | ||
protect.install = function() { | ||
installChildComponentsSync(protect); | ||
callChildComponentMethodsSync(protect, 'install'); | ||
}; | ||
@@ -42,0 +42,0 @@ |
@@ -20,3 +20,4 @@ /* | ||
BLOCKING_MODES, | ||
simpleTraverse, | ||
traverseKeysAndValues, | ||
traverseValues, | ||
Rule, | ||
@@ -203,3 +204,3 @@ isString, | ||
simpleTraverse(urlParams, function(path, type, value) { | ||
traverseValues(urlParams, function(path, type, value) { | ||
// url param names are not checked. | ||
@@ -311,3 +312,3 @@ if (type !== 'Value') { | ||
sourceContext.findings.bodyType = bodyType; | ||
sourceContext.bodyType = bodyType; | ||
@@ -379,10 +380,10 @@ if (block) { | ||
if (vpEvaluators.size === 1 && uuid) { | ||
if (!sourceContext.findings.serverFeaturesResultsMap[ruleId]) { | ||
sourceContext.findings.serverFeaturesResultsMap[ruleId] = []; | ||
if (!sourceContext.resultsMap[ruleId]) { | ||
sourceContext.resultsMap[ruleId] = []; | ||
} | ||
sourceContext.findings.serverFeaturesResultsMap[ruleId].push({ | ||
sourceContext.resultsMap[ruleId].push({ | ||
name, | ||
uuid | ||
}); | ||
sourceContext.findings.securityException = ['block', ruleId]; | ||
sourceContext.securityException = ['block', ruleId]; | ||
core.protect.throwSecurityException(sourceContext); | ||
@@ -419,7 +420,7 @@ } | ||
logger.info(match, 'Found a matching IP to an entry in ipDeny list'); | ||
if (!sourceContext.findings.serverFeaturesResultsMap[ruleId]) { | ||
sourceContext.findings.serverFeaturesResultsMap[ruleId] = []; | ||
if (!sourceContext.resultsMap[ruleId]) { | ||
sourceContext.resultsMap[ruleId] = []; | ||
} | ||
sourceContext.findings.serverFeaturesResultsMap[ruleId].push({ | ||
sourceContext.resultsMap[ruleId].push({ | ||
ip: match.matchedIp, | ||
@@ -442,3 +443,3 @@ uuid: match.uuid, | ||
const { resultsMap } = sourceContext.findings; | ||
const { resultsMap } = sourceContext; | ||
const probesRules = [Rule.CMD_INJECTION, Rule.PATH_TRAVERSAL, Rule.SQL_INJECTION, Rule.XXE]; | ||
@@ -453,7 +454,7 @@ const props = {}; | ||
blocked, | ||
details, | ||
exploitMetadata, | ||
value, | ||
inputType | ||
} = resultByRuleId; | ||
if (blocked || !blocked && details.length > 0 || !probesRules.some(rule => rule === ruleId)) return; | ||
if (blocked || !blocked && exploitMetadata.length > 0 || !probesRules.some(rule => rule === ruleId)) return; | ||
@@ -524,3 +525,3 @@ const { policy: { rulesMask } } = sourceContext; | ||
// or if none of the values of queryParams are objects. a quick '.includes()' | ||
// could be used to determine that. if none are objects then simpleTraverse() | ||
// could be used to determine that. if none are objects then traverseKeysAndValues() | ||
// wouldn't be used, just a simple "for (const key in queryParams) {...}" to | ||
@@ -531,3 +532,3 @@ // check each key and value associated with the key. | ||
// small, so it probably only makes sense to check if qs (or similar) is actually | ||
// in use. a benchmark of "for (const key in queryParams) {...}" vs simpleTraverse | ||
// in use. a benchmark of "for (const key in queryParams) {...}" vs traverseKeysAndValues | ||
// should be created to see if, and in what cases, it makes sense. | ||
@@ -538,3 +539,3 @@ // | ||
/* eslint-disable-next-line complexity */ | ||
simpleTraverse(object, function(path, type, value) { | ||
traverseKeysAndValues(object, function(path, type, value) { | ||
let itemType; | ||
@@ -721,6 +722,6 @@ let isMongoQueryType; | ||
function mergeFindings(sourceContext, newFindings) { | ||
const { findings, policy } = sourceContext; | ||
const { policy, securityException, resultsMap } = sourceContext; | ||
if (!newFindings.trackRequest) { | ||
return findings.securityException; | ||
return securityException; | ||
} | ||
@@ -734,14 +735,14 @@ | ||
findings.trackRequest = findings.trackRequest || newFindings.trackRequest; | ||
findings.securityException = findings.securityException || newFindings.securityException; | ||
sourceContext.trackRequest = sourceContext.trackRequest || newFindings.trackRequest; | ||
sourceContext.securityException = sourceContext.securityException || newFindings.securityException; | ||
// merge them into a ruleId-indexed map (pojo) | ||
for (const result of newFindings.resultsList) { | ||
if (!findings.resultsMap[result.ruleId]) { | ||
findings.resultsMap[result.ruleId] = []; | ||
if (!resultsMap[result.ruleId]) { | ||
resultsMap[result.ruleId] = []; | ||
} | ||
findings.resultsMap[result.ruleId].push(result); | ||
resultsMap[result.ruleId].push(result); | ||
} | ||
return findings.securityException; | ||
return sourceContext.securityException; | ||
} | ||
@@ -764,3 +765,3 @@ | ||
// sink analysis will add findings here | ||
r.details = []; | ||
r.exploitMetadata = []; | ||
@@ -767,0 +768,0 @@ // apply exclusions here. |
@@ -18,3 +18,3 @@ /* | ||
const { installChildComponentsSync } = require('@contrast/common'); | ||
const { callChildComponentMethodsSync } = require('@contrast/common'); | ||
@@ -51,3 +51,3 @@ module.exports = function(core) { | ||
inputAnalysis.install = function() { | ||
installChildComponentsSync(inputAnalysis); | ||
callChildComponentMethodsSync(inputAnalysis, 'install'); | ||
}; | ||
@@ -54,0 +54,0 @@ |
@@ -23,3 +23,4 @@ /* | ||
isString, | ||
simpleTraverse | ||
traverseKeys, | ||
traverseKeysAndValues, | ||
} = require('@contrast/common'); | ||
@@ -40,3 +41,3 @@ | ||
captureStacktrace(sinkContext, stacktraceData); | ||
result.details.push({ sinkContext, findings }); | ||
result.exploitMetadata.push({ sinkContext, findings }); | ||
@@ -48,3 +49,3 @@ const mode = sourceContext.policy[ruleId]; | ||
const blockInfo = [mode, ruleId]; | ||
sourceContext.findings.securityException = blockInfo; | ||
sourceContext.securityException = blockInfo; | ||
throwSecurityException(sourceContext); | ||
@@ -152,6 +153,9 @@ } | ||
if (typeof sinkContext.value === 'object') { | ||
simpleTraverse(sinkContext.value, function(path, type, value) { | ||
traverseKeysAndValues(sinkContext.value, function(path, type, value) { | ||
if (type !== 'Key' && !agentLib.isMongoQueryType(value)) return; | ||
stringFindings = handleStringValue(result, sinkContext.value[value], agentLib); | ||
// halt traversal | ||
return true; | ||
}); | ||
@@ -165,7 +169,7 @@ } else if (typeof sinkContext.value === 'string') { | ||
const nosqlInjectionResults = sourceContext.findings.resultsMap[ruleId]; | ||
const nosqlInjectionResults = sourceContext.resultsMap[ruleId]; | ||
if (Array.isArray(nosqlInjectionResults)) { | ||
nosqlInjectionResults.push(nosqlInjectionResult); | ||
} else { | ||
sourceContext.findings.resultsMap[ruleId] = [nosqlInjectionResult]; | ||
sourceContext.resultsMap[ruleId] = [nosqlInjectionResult]; | ||
} | ||
@@ -239,3 +243,3 @@ | ||
if (findings) { | ||
result.details.push({ sinkContext, findings }); | ||
result.exploitMetadata.push({ sinkContext, findings }); | ||
} | ||
@@ -259,14 +263,9 @@ } | ||
} | ||
return context.findings.resultsMap[ruleId]; | ||
return context.resultsMap[ruleId]; | ||
} | ||
function handleObjectValue(result, object) { | ||
if (typeof object !== 'object') { | ||
return null; | ||
} | ||
let findings = null; | ||
simpleTraverse(object, function(path, type, value) { | ||
if (type !== 'Key' || findings) { | ||
return; | ||
} | ||
traverseKeys(object, function(path, type, value) { | ||
// the result value is the key that was found | ||
@@ -286,2 +285,5 @@ if (result.key === value) { | ||
findings = { start, end, boundaryOverrunIndex: start, inputBoundaryIndex }; | ||
// halt traversal | ||
return true; | ||
} | ||
@@ -288,0 +290,0 @@ } |
@@ -18,3 +18,3 @@ /* | ||
const { installChildComponentsSync } = require('@contrast/common'); | ||
const { callChildComponentMethodsSync } = require('@contrast/common'); | ||
@@ -42,3 +42,3 @@ module.exports = function(core) { | ||
inputTracing.install = function() { | ||
installChildComponentsSync(inputTracing); | ||
callChildComponentMethodsSync(inputTracing, 'install'); | ||
}; | ||
@@ -45,0 +45,0 @@ |
@@ -26,6 +26,27 @@ /* | ||
depHooks, | ||
protect, | ||
protect: { inputTracing } | ||
protect: { getSourceContext, inputTracing } | ||
} = core; | ||
function pre({ args, name, hooked, orig }) { | ||
if (instrumentation.isLocked()) return; | ||
const sourceContext = getSourceContext('child_process'); | ||
const value = args[0]; | ||
if (!sourceContext || !value || !isString(value)) return; | ||
const sinkContext = { | ||
name, | ||
value, | ||
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] }, | ||
}; | ||
inputTracing.handleCommandInjection(sourceContext, sinkContext); | ||
core.protect.semanticAnalysis.handleCommandInjectionCommandBackdoors(sourceContext, sinkContext); | ||
core.protect.semanticAnalysis.handleCmdInjectionSemanticChainedCommands(sourceContext, sinkContext); | ||
core.protect.semanticAnalysis.handleCmdInjectionSemanticDangerous(sourceContext, sinkContext); | ||
core.protect.semanticAnalysis.handlePathTraversalFileSecurityBypass(sourceContext, sinkContext); | ||
} | ||
function install() { | ||
@@ -38,24 +59,3 @@ depHooks.resolve({ name: 'child_process' }, cp => { | ||
patchType, | ||
pre({ args, hooked, orig }) { | ||
if (instrumentation.isLocked()) return; | ||
const sourceContext = protect.getSourceContext('child_process'); | ||
const value = args[0]; | ||
if (!sourceContext || !value || !isString(value)) return; | ||
const sinkContext = { | ||
name, | ||
value, | ||
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] }, | ||
}; | ||
inputTracing.handleCommandInjection(sourceContext, sinkContext); | ||
// To evade code duplication we are using these INPUT TRACING instrumentation | ||
// to do the checks for SEMANTIC ANALYSIS too | ||
core.protect.semanticAnalysis.handleCommandInjectionCommandBackdoors(sourceContext, sinkContext); | ||
core.protect.semanticAnalysis.handleCmdInjectionSemanticChainedCommands(sourceContext, sinkContext); | ||
core.protect.semanticAnalysis.handleCmdInjectionSemanticDangerous(sourceContext, sinkContext); | ||
core.protect.semanticAnalysis.handlePathTraversalFileSecurityBypass(sourceContext, sinkContext); | ||
} | ||
pre | ||
}); | ||
@@ -62,0 +62,0 @@ }); |
@@ -53,3 +53,3 @@ /* | ||
}; | ||
console.log({ sinkContext: sinkContext.stacktraceData }); | ||
inputTracing.ssjsInjection(sourceContext, sinkContext); | ||
@@ -56,0 +56,0 @@ } |
@@ -17,12 +17,10 @@ /* | ||
'use strict'; | ||
const moduleName = 'mongodb'; | ||
const semver = require('semver'); | ||
const { patchType } = require('../constants'); | ||
const semver = require('semver'); | ||
module.exports = function (core) { | ||
const { | ||
depHooks, | ||
patcher, | ||
protect, | ||
protect: { inputTracing }, | ||
protect: { getSourceContext, inputTracing }, | ||
instrumentation: { instrument } | ||
} = core; | ||
@@ -60,175 +58,160 @@ | ||
function hookV3CommandAndCursor(obj, method, patchName, version) { | ||
patcher.patch(obj, method, { | ||
name: patchName, | ||
patchType, | ||
pre: ({ args, hooked, name, orig }) => { | ||
const value = getCursorQueryData(args, version); | ||
const sourceContext = protect.getSourceContext(patchName); | ||
function preHook(value, { name, hooked, orig }) { | ||
const sourceContext = getSourceContext(name); | ||
if (!sourceContext || !value) return; | ||
if (!sourceContext || !value) return; | ||
const sinkContext = { | ||
name, | ||
value, | ||
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] }, | ||
}; | ||
inputTracing.nosqlInjectionMongo(sourceContext, sinkContext); | ||
} | ||
}); | ||
const sinkContext = { | ||
name, | ||
value, | ||
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] }, | ||
}; | ||
inputTracing.nosqlInjectionMongo(sourceContext, sinkContext); | ||
} | ||
function hookV3UpdateAndRemove(obj, method, patchName) { | ||
patcher.patch(obj, method, { | ||
name: patchName, | ||
patchType, | ||
pre: ({ args, hooked, name, orig }) => { | ||
const sourceContext = protect.getSourceContext(patchName); | ||
function v4CollectionVal({ args, ...ctx }) { | ||
const value = typeof args[0] == 'function' ? null : args[0]; | ||
preHook(value, ctx); | ||
} | ||
if (!sourceContext) return; | ||
function v4DbVal({ args, ...ctx }) { | ||
const value = args[0]?.filter; | ||
preHook(value, ctx); | ||
} | ||
const ops = Array.isArray(args[1]) | ||
? args[1] | ||
: [args[1]]; | ||
for (const op of ops) { | ||
const value = op && getOpQueryData(op); | ||
if (value) { | ||
const sinkContext = { | ||
name, | ||
value, | ||
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] }, | ||
}; | ||
inputTracing.nosqlInjectionMongo(sourceContext, sinkContext); | ||
} | ||
} | ||
}, | ||
}); | ||
function v4CursorVal({ args, ...ctx }) { | ||
const value = args[2]; | ||
preHook(value, ctx); | ||
} | ||
function v3CursorVal({ args, ...ctx }, version) { | ||
const value = getCursorQueryData(args, version); | ||
preHook(value, ctx); | ||
} | ||
function v3DbVal({ args, ...ctx }) { | ||
const value = args[0]; | ||
preHook(value, ctx); | ||
} | ||
function v3TopologyVal({ args, ...ctx }) { | ||
const ops = Array.isArray(args[1]) ? args[1] : [args[1]]; | ||
for (const op of ops) { | ||
const value = op && getOpQueryData(op); | ||
if (value) { | ||
preHook(value, ctx); | ||
} | ||
} | ||
} | ||
function install() { | ||
const v4MethodsWithFilter = [ | ||
'updateOne', | ||
'replaceOne', | ||
'updateMany', | ||
'deleteOne', | ||
'deleteMany', | ||
'findOneAndDelete', | ||
'findOneAndReplace', | ||
'findOneAndUpdate', | ||
'countDocuments', | ||
'count', | ||
'distinct', | ||
]; | ||
depHooks.resolve( | ||
[ | ||
{ | ||
name: 'mongodb', version: '>=4.0.0' | ||
moduleName, | ||
version: '>=4.0.0', | ||
patchObjects: [ | ||
{ | ||
name: 'Collection.prototype', | ||
methods: [ | ||
'updateOne', | ||
'replaceOne', | ||
'updateMany', | ||
'deleteOne', | ||
'deleteMany', | ||
'findOneAndDelete', | ||
'findOneAndReplace', | ||
'findOneAndUpdate', | ||
'countDocuments', | ||
'count', | ||
'distinct', | ||
], | ||
patchType, | ||
preHookFn: v4CollectionVal | ||
}, | ||
{ | ||
name: 'Db.prototype', | ||
methods: ['command'], | ||
patchType, | ||
preHookFn: v4DbVal | ||
} | ||
] | ||
}, | ||
(mongodb) => { | ||
v4MethodsWithFilter.forEach((method) => { | ||
patcher.patch(mongodb.Collection.prototype, method, { | ||
name: `mongodb.Collection.prototype.${method}`, | ||
{ | ||
moduleName, | ||
version: '>=4.0.0', | ||
file: 'lib/cursor/find_cursor', | ||
patchObjects: [ | ||
{ | ||
methods: ['FindCursor'], | ||
patchType, | ||
pre: ({ args, hooked, name, orig }) => { | ||
const value = typeof args[0] == 'function' ? null : args[0]; | ||
const sourceContext = protect.getSourceContext(`mongodb.Collection.prototype.${method}`); | ||
if (!sourceContext || !value) return; | ||
const sinkContext = { | ||
name, | ||
value, | ||
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] }, | ||
}; | ||
inputTracing.nosqlInjectionMongo(sourceContext, sinkContext); | ||
}, | ||
}); | ||
}); | ||
patcher.patch(mongodb.Db.prototype, 'command', { | ||
name: 'mongodb.Db.prototype.command', | ||
patchType, | ||
pre: ({ args, hooked, name, orig }) => { | ||
const value = args[0]?.filter; | ||
const sourceContext = protect.getSourceContext('mongodb.Collection.prototype.command'); | ||
if (!sourceContext || !value) return; | ||
const sinkContext = { | ||
name, | ||
value, | ||
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] }, | ||
}; | ||
inputTracing.nosqlInjectionMongo(sourceContext, sinkContext); | ||
preHookFn: v4CursorVal | ||
} | ||
}); | ||
}); | ||
depHooks.resolve( | ||
] | ||
}, | ||
{ | ||
name: 'mongodb', version: '<4.0.0' | ||
}, (mongodb, { version }) => { | ||
hookV3CommandAndCursor(mongodb.CoreServer.prototype, 'cursor', 'mongodb.CoreServer.prototype.cursor', version); | ||
patcher.patch(mongodb.Db.prototype, 'eval', { | ||
name: 'mongodb.Db.prototype.eval', | ||
patchType, | ||
pre: ({ args, hooked, name, orig }) => { | ||
const value = args[0]; | ||
const sourceContext = protect.getSourceContext('mongodb.Db.prototype.eval'); | ||
if (!sourceContext || !value) return; | ||
const sinkContext = { | ||
name, | ||
value, | ||
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] }, | ||
}; | ||
inputTracing.nosqlInjectionMongo(sourceContext, sinkContext); | ||
moduleName, | ||
version: '<4.0.0', | ||
patchObjects: [ | ||
{ | ||
name: 'CoreServer.prototype', | ||
methods: ['cursor'], | ||
patchType, | ||
preHookFn: v3CursorVal | ||
}, | ||
{ | ||
name: 'Db.prototype', | ||
methods: ['eval'], | ||
patchType, | ||
preHookFn: v3DbVal | ||
} | ||
}); | ||
}); | ||
depHooks.resolve({ name: 'mongodb', file: 'lib/cursor/find_cursor', version: '>=4.0.0' }, (cursor) => patcher.patch(cursor, 'FindCursor', { | ||
name: 'mongodb.FindCursor', | ||
patchType, | ||
pre: ({ args, hooked, name, orig }) => { | ||
const value = args[2]; | ||
const sourceContext = protect.getSourceContext('mongodb.FindCursor'); | ||
if (!sourceContext || !value) return; | ||
const sinkContext = { | ||
name, | ||
value, | ||
stacktraceData: { constructorOpt: hooked, prependFrames: [orig] }, | ||
}; | ||
inputTracing.nosqlInjectionMongo(sourceContext, sinkContext); | ||
] | ||
}, | ||
{ | ||
moduleName, | ||
file: 'lib/topologies/topology_base.js', | ||
version: '<4.0.0', | ||
patchObjects: [ | ||
{ | ||
name: 'TopologyBase.prototype', | ||
methods: ['update', 'remove'], | ||
patchType, | ||
preHookFn: v3TopologyVal | ||
}, | ||
{ | ||
name: 'TopologyBase.prototype', | ||
methods: ['command'], | ||
patchType, | ||
preHookFn: v3CursorVal | ||
} | ||
] | ||
}, | ||
{ | ||
moduleName, | ||
file: 'lib/topologies/native_topology.js', | ||
version: '<4.0.0', | ||
patchObjects: [ | ||
{ | ||
name: 'NativeTopology.prototype', | ||
patchName: 'prototype', | ||
methods: ['update', 'remove'], | ||
patchType, | ||
preHookFn: v3TopologyVal | ||
}, | ||
{ | ||
name: 'NativeTopology.prototype', | ||
patchName: 'prototype', | ||
methods: ['command'], | ||
patchType, | ||
preHookFn: v3CursorVal | ||
} | ||
] | ||
} | ||
})); | ||
const mongoDBTopologiesMethods = ['update', 'remove']; | ||
depHooks.resolve({ | ||
name: 'mongodb', | ||
file: 'lib/topologies/topology_base.js', | ||
version: '<4.0.0' | ||
}, (tpl, { version }) => { | ||
mongoDBTopologiesMethods.forEach((method) => { | ||
hookV3UpdateAndRemove(tpl.TopologyBase.prototype, method, `mongodb.TopologyBase.prototype.${method}`); | ||
].forEach(({ moduleName, file, version, patchObjects }) => { | ||
instrument({ | ||
moduleName, | ||
file, | ||
version, | ||
patchObjects | ||
}); | ||
hookV3CommandAndCursor(tpl.TopologyBase.prototype, 'command', 'mongodb.TopologyBase.prototype.command', version); | ||
}); | ||
depHooks.resolve({ | ||
name: 'mongodb', | ||
file: 'lib/topologies/native_topology.js', | ||
version: '<4.0.0' | ||
}, (NativeTopology, { version }) => { | ||
mongoDBTopologiesMethods.forEach((method) => { | ||
hookV3UpdateAndRemove(NativeTopology.prototype, method, `mongodb.NativeTopology.prototype.${method}`); | ||
}); | ||
hookV3CommandAndCursor(NativeTopology.prototype, 'command', 'mongodb.NativeTopology.prototype.command', version); | ||
}); | ||
} | ||
@@ -235,0 +218,0 @@ const mongodbInstr = (core.protect.inputTracing.mongodbInstrumentation = { |
@@ -90,15 +90,8 @@ /* | ||
// maybe better as result, findings... but my bad naming choice is | ||
// past the point of return. | ||
findings: { | ||
trackRequest: false, | ||
securityException: undefined, | ||
// bodyType is set to a body type if handlers.parseRawBody() parsed it | ||
// successfully. | ||
bodyType: undefined, | ||
resultsMap: Object.create(null), | ||
hardeningResultsMap: Object.create(null), | ||
semanticResultsMap: Object.create(null), | ||
serverFeaturesResultsMap: Object.create(null) | ||
}, | ||
trackRequest: false, | ||
securityException: undefined, | ||
// bodyType is set to a body type if handlers.parseRawBody() parsed it | ||
// successfully. | ||
bodyType: undefined, | ||
resultsMap: Object.create(null), | ||
}; | ||
@@ -105,0 +98,0 @@ |
@@ -23,3 +23,3 @@ /* | ||
InputType, | ||
simpleTraverse | ||
traverseValues, | ||
} = require('@contrast/common'); | ||
@@ -49,3 +49,6 @@ | ||
blocked: false, | ||
findings: { command: value }, | ||
ruleId, | ||
value, | ||
mappedId: ruleId, | ||
exploitMetadata: [{ command: value }], | ||
sinkContext, | ||
@@ -55,3 +58,3 @@ ...finding | ||
getRuleResults(sourceContext.findings.semanticResultsMap, ruleId).push(result); | ||
getRuleResults(sourceContext.resultsMap, ruleId).push(result); | ||
@@ -61,3 +64,3 @@ if (BLOCKING_MODES.includes(mode)) { | ||
const blockInfo = [mode, ruleId]; | ||
sourceContext.findings.securityException = blockInfo; | ||
sourceContext.securityException = blockInfo; | ||
throwSecurityException(sourceContext); | ||
@@ -110,3 +113,3 @@ } | ||
handleResult(sourceContext, sinkContext, Rule.PATH_TRAVERSAL_SEMANTIC_FILE_SECURITY_BYPASS, mode, { | ||
findings: { path: sinkContext.value } | ||
exploitMetadata: [{ path: sinkContext.value }] | ||
}); | ||
@@ -123,3 +126,3 @@ } | ||
handleResult(sourceContext, sinkContext, Rule.XXE, mode, { | ||
findings, | ||
exploitMetadata: [findings], | ||
}); | ||
@@ -152,13 +155,11 @@ } | ||
let found = false; | ||
let found; | ||
for (const inputType in valuesOfInterest) { | ||
if (found) break; | ||
const values = valuesOfInterest[inputType]; | ||
if (values && Object.keys(values).length) { | ||
simpleTraverse(values, (path, type, value, obj) => { | ||
if ( | ||
!found && | ||
type === 'Value' && | ||
isBackdoorDetected(value, command) | ||
) { | ||
traverseValues(values, (path, type, value, obj) => { | ||
if (isBackdoorDetected(value, command)) { | ||
let key; | ||
@@ -177,2 +178,5 @@ if (inputType === InputType.HEADER) { | ||
}; | ||
// halt traversal | ||
return true; | ||
} | ||
@@ -179,0 +183,0 @@ }); |
@@ -18,3 +18,3 @@ /* | ||
const { installChildComponentsSync } = require('@contrast/common'); | ||
const { callChildComponentMethodsSync } = require('@contrast/common'); | ||
@@ -41,9 +41,6 @@ /** | ||
semanticAnalysis.install = function() { | ||
installChildComponentsSync(semanticAnalysis); | ||
callChildComponentMethodsSync(semanticAnalysis, 'install'); | ||
}; | ||
// There is no `.install()` method as this STAGE does not introduce side effects on its own, | ||
// it uses the instrumentation that's already in place for INPUT TRACING. | ||
return semanticAnalysis; | ||
}; |
@@ -27,5 +27,3 @@ /* | ||
const { | ||
findings: { | ||
securityException: [mode, ruleId] | ||
} | ||
securityException: [mode, ruleId] | ||
} = sourceContext; | ||
@@ -32,0 +30,0 @@ |
{ | ||
"name": "@contrast/protect", | ||
"version": "1.8.1", | ||
"version": "1.9.0", | ||
"description": "Contrast service providing framework-agnostic Protect support", | ||
@@ -21,5 +21,5 @@ "license": "SEE LICENSE IN LICENSE", | ||
"@contrast/agent-lib": "^5.1.0", | ||
"@contrast/common": "1.1.5", | ||
"@contrast/core": "1.7.1", | ||
"@contrast/esm-hooks": "1.3.1", | ||
"@contrast/common": "1.2.0", | ||
"@contrast/core": "1.8.0", | ||
"@contrast/esm-hooks": "1.4.0", | ||
"@contrast/scopes": "1.2.0", | ||
@@ -29,2 +29,2 @@ "ipaddr.js": "^2.0.1", | ||
} | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
185014
4858
+ Added@contrast/agentify@1.2.0(transitive)
+ Added@contrast/common@1.2.0(transitive)
+ Added@contrast/config@1.4.0(transitive)
+ Added@contrast/core@1.8.0(transitive)
+ Added@contrast/deadzones@1.0.0(transitive)
+ Added@contrast/esm-hooks@1.4.0(transitive)
+ Added@contrast/instrumentation@1.0.0(transitive)
+ Added@contrast/patcher@1.1.0(transitive)
+ Added@contrast/reporter@1.7.0(transitive)
- Removed@contrast/agentify@1.1.2(transitive)
- Removed@contrast/common@1.1.5(transitive)
- Removed@contrast/config@1.3.3(transitive)
- Removed@contrast/core@1.7.1(transitive)
- Removed@contrast/esm-hooks@1.3.1(transitive)
- Removed@contrast/patcher@1.0.6(transitive)
- Removed@contrast/reporter@1.6.2(transitive)
Updated@contrast/common@1.2.0
Updated@contrast/core@1.8.0
Updated@contrast/esm-hooks@1.4.0