@contrast/protect 1.19.0 → 1.21.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.
@@ -19,64 +19,72 @@ const { isString } = require('@contrast/common');
19
19
  const SecurityException = require('../../security-exception');
20
20
  const { patchType } = require('../constants');
21
21
 
22
- module.exports = function(core) {
22
+ module.exports = function (core) {
23
23
  const {
24
24
  depHooks,
25
25
  patcher,
26
26
  logger,
27
- protect: { semanticAnalysis },
28
27
  protect,
29
28
  } = core;
30
29
 
31
- function install() {
32
- depHooks.resolve({ name: 'libxmljs' }, hookLibXml);
33
- // libxmljs2 is a fork of libxml that has identical signatures.
34
- // the only difference is that libxmljs2 is used by juice-shop and thus
35
- // we're missing sinks in one of our most important sample apps.
36
- depHooks.resolve({ name: 'libxmljs2' }, hookLibXml);
37
- }
30
+ /**
31
+ * @param {boolean} newApi
32
+ */
33
+ const handler = (newApi) => (mod, metadata) => {
34
+ const checkOptions = newApi
35
+ ? (opts) => !opts?.noent && !opts?.replaceEntities
36
+ : (opts) => !opts?.noent;
38
37
 
39
- function hookLibXml(libxmljs) {
40
- patcher.patch(libxmljs, 'parseXmlString', {
41
- name: 'libxmljs.parseXmlString',
42
- patchType,
43
- pre: preParseXmlMethod
44
- });
38
+ const methods = newApi
39
+ ? ['parseXml', 'parseXmlAsync']
40
+ : ['parseXml', 'parseXmlString'];
45
41
 
46
- patcher.patch(libxmljs, 'parseXml', {
47
- name: 'libxmljs.parseXml',
48
- patchType,
49
- pre: preParseXmlMethod
50
- });
51
- }
42
+ methods.forEach((method) => {
43
+ const name = `${metadata.name}:${method}`;
44
+ patcher.patch(mod, method, {
45
+ name,
46
+ patchType,
47
+ pre({ args, hooked, orig }) {
48
+ const sourceContext = protect.getSourceContext(name);
49
+ const [value, options] = args;
52
50
 
53
- function preParseXmlMethod({ args, hooked, orig }) {
54
- const sourceContext = protect.getSourceContext('libxmljs.parseXmlString');
55
- const value = args[0];
51
+ if (!sourceContext || !value || !isString(value) || checkOptions(options)) {
52
+ return;
53
+ }
56
54
 
57
- // If noent isn't set to true than libxml won't load
58
- // external entities, so this isn't vulnerable
59
- // see: https://help.semmle.com/wiki/display/JS/XML+external+entity+expansion
60
- if (!sourceContext || !value || !isString(value) || !args[1].noent) return;
55
+ const sinkContext = {
56
+ name,
57
+ value,
58
+ stacktraceOpts: {
59
+ constructorOpt: hooked,
60
+ prependFrames: [orig]
61
+ },
62
+ };
61
63
 
62
- const sinkContext = {
63
- name: 'libxmljs.parseXmlString',
64
- value,
65
- stacktraceOpts: { constructorOpt: hooked, prependFrames: [orig] },
66
- };
64
+ try {
65
+ protect.semanticAnalysis.handleXXE(sourceContext, sinkContext);
66
+ } catch (err) {
67
+ if (SecurityException.isSecurityException(err)) {
68
+ throw err;
69
+ }
67
70
 
68
- try {
69
- semanticAnalysis.handleXXE(sourceContext, sinkContext);
70
- } catch (err) {
71
- if (SecurityException.isSecurityException(err)) {
72
- throw err;
73
- }
71
+ logger.error({ err }, 'Unexpected error during semantic analysis');
72
+ }
73
+ }
74
+ });
75
+ });
76
+ };
74
77
 
75
- logger.error({ err }, 'Unexpected error during semantic analysis');
76
- }
77
- }
78
+ protect.semanticAnalysis.libxmljs = {
79
+ install() {
80
+ // libxmljs changed its API in version 1.0.0
81
+ depHooks.resolve({ name: 'libxmljs', version: '>=1' }, handler(true));
78
82
 
79
- const libxmljs = semanticAnalysis.libxmljs = { install };
83
+ // libxmljs versions prior to 1.0.0 and libxmljs2 share the same API
84
+ depHooks.resolve({ name: 'libxmljs', version: '<1' }, handler(false));
85
+ depHooks.resolve({ name: 'libxmljs2' }, handler(false));
86
+ }
87
+ };
80
88
 
81
- return libxmljs;
89
+ return protect.semanticAnalysis.libxmljs;
82
90
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/protect",
3
- "version": "1.19.0",
3
+ "version": "1.21.0",
4
4
  "description": "Contrast service providing framework-agnostic Protect support",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
@@ -18,9 +18,9 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@contrast/agent-lib": "^7.0.1",
21
- "@contrast/common": "1.10.0",
22
- "@contrast/core": "1.17.0",
23
- "@contrast/esm-hooks": "1.13.0",
21
+ "@contrast/common": "1.12.0",
22
+ "@contrast/core": "1.19.0",
23
+ "@contrast/esm-hooks": "1.15.0",
24
24
  "@contrast/scopes": "1.4.0",
25
25
  "ipaddr.js": "^2.0.1",
26
26
  "semver": "^7.3.7"