@contrast/assess
Advanced tools
Comparing version 1.25.0 to 1.26.0
@@ -40,3 +40,4 @@ /* | ||
const REWRITE_OPTS = { inject: false, wrap: false }; | ||
/** @type {import('@contrast/rewriter').RewriteOpts} */ | ||
const REWRITE_OPTS = { isModule: false, inject: false, wrap: false, trim: true }; | ||
const WRAPPER_PREFIX = join([ | ||
@@ -67,3 +68,3 @@ 'function tempWrapper() {', | ||
try { | ||
const { code } = rewriter.rewrite(`${WRAPPER_PREFIX}${data.obj.source}${WRAPPER_SUFFIX}`, REWRITE_OPTS); | ||
const { code } = rewriter.rewriteSync(`${WRAPPER_PREFIX}${data.obj.source}${WRAPPER_SUFFIX}`, REWRITE_OPTS); | ||
data.obj.source = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}')); | ||
@@ -70,0 +71,0 @@ } catch (err) { |
@@ -19,2 +19,5 @@ /* | ||
/** @type {import('@contrast/rewriter').RewriteOpts} */ | ||
const REWRITE_OPTS = { isModule: false, inject: false, wrap: false, trim: true }; | ||
module.exports = function (core) { | ||
@@ -27,4 +30,2 @@ const store = { lock: true, name: 'assess:propagators:pug-compile' }; | ||
const rewriterOpts = { isModule: false, wrap: false }; | ||
const pugInstrumentation = { | ||
@@ -47,3 +48,3 @@ install() { | ||
return instrumentation.run(store, | ||
() => rewriter.rewrite(value, rewriterOpts).code | ||
() => rewriter.rewriteSync(value, REWRITE_OPTS).code | ||
); | ||
@@ -50,0 +51,0 @@ } catch (err) { |
@@ -23,2 +23,4 @@ /* | ||
Event, | ||
join, | ||
toLowerCase, | ||
} = require('@contrast/common'); | ||
@@ -32,2 +34,34 @@ | ||
function buildUriPathRegExp(urls) { | ||
let regExpNeeded = false; | ||
for (const url of urls) { | ||
if (regExpCheck(url)) { | ||
regExpNeeded = true; | ||
} | ||
} | ||
if (regExpNeeded) { | ||
const rx = new RegExp(`^${join(urls, '|')}$`); | ||
return (uriPath) => rx ? rx.test(uriPath) : false; | ||
} | ||
return (uriPath) => urls.some((url) => url === uriPath); | ||
} | ||
function createUriPathMatcher(urls) { | ||
if (urls.length) { | ||
return buildUriPathRegExp(urls); | ||
} else { | ||
return () => true; | ||
} | ||
} | ||
function regExpCheck(str) { | ||
return str.indexOf('*') > 0 || | ||
str.indexOf('.') > 0 || | ||
str.indexOf('+') > 0 || | ||
str.indexOf('?') > 0 || | ||
str.indexOf('\\') > 0; | ||
} | ||
/** | ||
@@ -42,30 +76,100 @@ * @param {{ | ||
module.exports = function assess(core) { | ||
const { logger, messages } = core; | ||
const { config, logger, messages } = core; | ||
const enabledRules = new Set(rulesIds); | ||
const exclusions = { | ||
url: [], | ||
}; | ||
function compileExclusions(settings) { | ||
// reset global exclusion state | ||
for (const key of Object.keys(exclusions)) { | ||
exclusions[key] = []; | ||
} | ||
const rawDtmList = [ | ||
// todo: NODE-3281 input exclusions | ||
...(settings?.exclusions?.url || []) | ||
].filter((exclusion) => exclusion.modes.includes('assess')); | ||
if (!rawDtmList.length) { | ||
return; | ||
} | ||
for (const dtm of rawDtmList) { | ||
dtm.type = dtm.type || 'URL'; | ||
const { name, assess_rules, urls, type } = dtm; | ||
const key = toLowerCase(type); | ||
try { | ||
const e = { | ||
name, | ||
rules: new Set(assess_rules), | ||
}; | ||
e.matchesUriPath = createUriPathMatcher(urls); | ||
exclusions[key].push(e); | ||
} catch (err) { | ||
logger.error({ err, dtm }, 'failed to process exclusion'); | ||
} | ||
} | ||
} | ||
messages.on(Event.SERVER_SETTINGS_UPDATE, (msg) => { | ||
if (!msg.assess) return; | ||
if (!config.getEffectiveValue('assess.enable')) return; | ||
for (const ruleId of rulesIds) { | ||
const enable = msg.assess[ruleId]?.enable; | ||
if (enable === true) { | ||
enabledRules.add(ruleId); | ||
if (ruleId === Rule.NOSQL_INJECTION) enabledRules.add(Rule.NOSQL_INJECTION_MONGO); | ||
} else if (enable === false) { | ||
if (ruleId === Rule.NOSQL_INJECTION) enabledRules.delete(Rule.NOSQL_INJECTION_MONGO); | ||
enabledRules.delete(ruleId); | ||
if (msg.assess) { | ||
for (const ruleId of rulesIds) { | ||
const enable = msg.assess[ruleId]?.enable; | ||
if (enable === true) { | ||
enabledRules.add(ruleId); | ||
if (ruleId === Rule.NOSQL_INJECTION) enabledRules.add(Rule.NOSQL_INJECTION_MONGO); | ||
} else if (enable === false) { | ||
if (ruleId === Rule.NOSQL_INJECTION) enabledRules.delete(Rule.NOSQL_INJECTION_MONGO); | ||
enabledRules.delete(ruleId); | ||
} | ||
} | ||
} | ||
logger.info({ | ||
enabledRules: Array.from(enabledRules) | ||
}, 'Assess policy updated'); | ||
if (msg.exclusions) { | ||
compileExclusions(msg); | ||
} | ||
logger.info({ enabledRules: Array.from(enabledRules) }, 'Assess policy updated'); | ||
}); | ||
return core.assess.getPolicy = function getPolicy() { | ||
/** | ||
* This gets called by assess.makeSourceContext(). We return copy of policy to avoid | ||
* inconsistent behavior if policy is updated during request handling. | ||
*/ | ||
return core.assess.getPolicy = function getPolicy({ uriPath } = {}) { | ||
const _enabledRules = new Set(enabledRules); | ||
// Evaluate url exclusions for current request. | ||
// If one matches and applies to all rules, we return `null` for the policy value, | ||
// which will disable assess for the request. If specific rules are disabled, we | ||
// just removed them from the request policy's set of `enabledRules`. | ||
for (const urlExclusion of exclusions.url) { | ||
if (urlExclusion.matchesUriPath(uriPath)) { | ||
if (!urlExclusion.rules?.size) { | ||
core.logger.debug({ | ||
name: urlExclusion.name | ||
}, 'all assess rules disabled by URL exclusion'); | ||
return null; | ||
} else { | ||
for (const ruleId of urlExclusion.rules) { | ||
if (_enabledRules.has(ruleId)) _enabledRules.delete(ruleId); | ||
} | ||
core.logger.debug({ | ||
name: urlExclusion.name, | ||
rules: Array.from(urlExclusion.rules), | ||
}, 'assess rules disabled by URL exclusion'); | ||
} | ||
} | ||
} | ||
// creates copy of local policy for request store | ||
return { | ||
enabledRules: new Set(enabledRules), | ||
enabledRules: new Set(_enabledRules), | ||
}; | ||
}; | ||
}; |
@@ -41,3 +41,4 @@ /* | ||
if (!ctx || instrumentation.isLocked()) return null; | ||
// policy will not exist if assess is altogether disabled for the active request e.g. url exclusion | ||
if (!ctx || !ctx?.policy || instrumentation.isLocked()) return null; | ||
@@ -44,0 +45,0 @@ switch (type) { |
@@ -57,3 +57,3 @@ /* | ||
sourceEventsCount: 0, | ||
policy: getPolicy(), | ||
policy: getPolicy({ uriPath }), | ||
reqData: { | ||
@@ -60,0 +60,0 @@ ip: req.socket.remoteAddress, |
{ | ||
"name": "@contrast/assess", | ||
"version": "1.25.0", | ||
"version": "1.26.0", | ||
"description": "Contrast service providing framework-agnostic Assess support", | ||
@@ -5,0 +5,0 @@ "license": "SEE LICENSE IN LICENSE", |
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
585868
16779