botium-core 1.13.19 → 1.14.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.
- package/dist/botium-cjs.js +298 -382
- package/dist/botium-cjs.js.map +1 -1
- package/dist/botium-es.js +298 -380
- package/dist/botium-es.js.map +1 -1
- package/package.json +20 -25
- package/samples/extensions/asserterHooks/botium.json +1 -0
- package/samples/extensions/logichooks/botium.json +1 -0
- package/samples/extensions/logichooks/custom/MyAsserter.js +3 -3
- package/src/Capabilities.js +2 -1
- package/src/containers/PluginConnectorContainer.js +8 -0
- package/src/containers/plugins/SimpleRestContainer.js +17 -9
- package/src/containers/plugins/index.js +29 -41
- package/src/helpers/HookUtils.js +32 -68
- package/src/scripting/ScriptingMemory.js +0 -24
- package/src/scripting/logichook/LogicHookUtils.js +27 -47
- package/test/compiler/precompilerscript.spec.js +24 -26
- package/test/connectors/pluginconnectorcontainer.spec.js +60 -0
- package/test/connectors/simplerest.spec.js +24 -27
- package/test/convo/fillAndApplyScriptingMemory.spec.js +1 -47
- package/test/hooks/customhooks.spec.js +3 -25
- package/test/logichooks/hookfromsrc.spec.js +13 -3
- package/test/plugins/plugins.spec.js +29 -2
- package/test/security/allowUnsafe.spec.js +20 -129
- package/LICENSES-3RDPARTY.txt +0 -6450
- package/test/scripting/asserters/convos/customembeddedasserterwithhugo.convo.txt +0 -7
- package/test/scripting/asserters/convos/customembeddedasserterwithouthugo.convo.txt +0 -7
- package/test/scripting/asserters/customEmbeddedAsserter.json +0 -14
- package/test/scripting/asserters/customEmbeddedAsserter.spec.js +0 -55
- package/test/scripting/logichooks/convos/custom_embedded.convo.txt +0 -8
- package/test/scripting/logichooks/customEmbedded.json +0 -14
- package/test/scripting/logichooks/customEmbedded.spec.js +0 -44
- package/test/security/convos/withscriptingmemoryfunction.convo.txt +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "botium-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "The Selenium for Chatbots",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "dist/botium-es.js",
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"link": "npm link botium-connector-dialogflow botium-connector-webdriverio botium-connector-directline3 botium-connector-watson botium-connector-alexa-smapi botium-connector-echo",
|
|
19
19
|
"test": "cross-env NODE_PATH=\"./test/plugins/plugindir/fromfolder:./test/plugins/plugindir/fromfile:./test/security/resources\" mocha \"./test/**/*.spec.js\"",
|
|
20
20
|
"coverage:report": "nyc report --reporter=lcov npm test",
|
|
21
|
-
"license-checker": "license-checker > LICENSES-3RDPARTY.txt",
|
|
22
21
|
"update-dependencies": "npm-check-updates --reject globby,rollup -u --timeout 120000"
|
|
23
22
|
},
|
|
24
23
|
"repository": {
|
|
@@ -32,14 +31,13 @@
|
|
|
32
31
|
},
|
|
33
32
|
"homepage": "https://www.botium.ai",
|
|
34
33
|
"dependencies": {
|
|
35
|
-
"@babel/runtime": "^7.
|
|
34
|
+
"@babel/runtime": "^7.22.15",
|
|
36
35
|
"async": "^3.2.4",
|
|
37
36
|
"body-parser": "^1.20.2",
|
|
38
37
|
"boolean": "^3.2.0",
|
|
39
38
|
"bottleneck": "^2.19.5",
|
|
40
|
-
"csv-parse": "^5.
|
|
39
|
+
"csv-parse": "^5.5.0",
|
|
41
40
|
"debug": "^4.3.4",
|
|
42
|
-
"esprima": "^4.0.1",
|
|
43
41
|
"express": "^4.18.2",
|
|
44
42
|
"globby": "11.0.4",
|
|
45
43
|
"ioredis": "^5.3.2",
|
|
@@ -54,45 +52,42 @@
|
|
|
54
52
|
"moment-timezone": "^0.5.43",
|
|
55
53
|
"mustache": "^4.2.0",
|
|
56
54
|
"promise-retry": "^2.0.1",
|
|
57
|
-
"promise.allsettled": "^1.0.
|
|
55
|
+
"promise.allsettled": "^1.0.7",
|
|
58
56
|
"randomatic": "^3.1.1",
|
|
59
57
|
"request": "^2.88.2",
|
|
60
|
-
"rimraf": "^5.0.
|
|
58
|
+
"rimraf": "^5.0.1",
|
|
61
59
|
"sanitize-filename": "^1.6.3",
|
|
62
60
|
"slugify": "^1.6.6",
|
|
63
|
-
"socket.io": "^4.
|
|
64
|
-
"socket.io-client": "^4.
|
|
61
|
+
"socket.io": "^4.7.2",
|
|
62
|
+
"socket.io-client": "^4.7.2",
|
|
65
63
|
"socketio-auth": "^0.1.1",
|
|
66
64
|
"swagger-jsdoc": "^6.2.8",
|
|
67
|
-
"swagger-ui-express": "^
|
|
65
|
+
"swagger-ui-express": "^5.0.0",
|
|
68
66
|
"uuid": "^9.0.0",
|
|
69
|
-
"vm2": "^3.9.17",
|
|
70
67
|
"word-error-rate": "0.0.7",
|
|
71
68
|
"write-yaml": "^1.0.0",
|
|
72
69
|
"xlsx": "^0.18.5",
|
|
73
70
|
"xregexp": "^5.1.1",
|
|
74
|
-
"yaml": "^2.
|
|
71
|
+
"yaml": "^2.3.2"
|
|
75
72
|
},
|
|
76
73
|
"devDependencies": {
|
|
77
|
-
"@babel/core": "^7.
|
|
78
|
-
"@babel/node": "^7.
|
|
79
|
-
"@babel/plugin-transform-runtime": "^7.
|
|
80
|
-
"@babel/preset-env": "^7.
|
|
81
|
-
"chai": "^4.3.
|
|
74
|
+
"@babel/core": "^7.22.17",
|
|
75
|
+
"@babel/node": "^7.22.15",
|
|
76
|
+
"@babel/plugin-transform-runtime": "^7.22.15",
|
|
77
|
+
"@babel/preset-env": "^7.22.15",
|
|
78
|
+
"chai": "^4.3.8",
|
|
82
79
|
"chai-as-promised": "^7.1.1",
|
|
83
80
|
"cross-env": "^7.0.3",
|
|
84
|
-
"eslint": "^8.
|
|
85
|
-
"eslint-config-standard": "^17.
|
|
86
|
-
"eslint-plugin-import": "^2.
|
|
81
|
+
"eslint": "^8.49.0",
|
|
82
|
+
"eslint-config-standard": "^17.1.0",
|
|
83
|
+
"eslint-plugin-import": "^2.28.1",
|
|
87
84
|
"eslint-plugin-mocha": "^10.1.0",
|
|
88
|
-
"eslint-plugin-n": "^
|
|
85
|
+
"eslint-plugin-n": "^16.1.0",
|
|
89
86
|
"eslint-plugin-promise": "^6.1.1",
|
|
90
87
|
"eslint-plugin-standard": "^4.1.0",
|
|
91
|
-
"license-checker": "^25.0.1",
|
|
92
|
-
"license-compatibility-checker": "^0.3.5",
|
|
93
88
|
"mocha": "^10.2.0",
|
|
94
|
-
"nock": "^13.3.
|
|
95
|
-
"npm-check-updates": "^16.
|
|
89
|
+
"nock": "^13.3.3",
|
|
90
|
+
"npm-check-updates": "^16.13.3",
|
|
96
91
|
"nyc": "^15.1.0",
|
|
97
92
|
"rollup": "2.79.1",
|
|
98
93
|
"rollup-plugin-babel": "^4.4.0",
|
|
@@ -6,17 +6,17 @@ module.exports = class MyAsserter {
|
|
|
6
6
|
this.caps = caps
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
assertConvoBegin ({convo, container, args}) {
|
|
9
|
+
assertConvoBegin ({ convo, container, args }) {
|
|
10
10
|
console.log(`MyAsserter assertConvoBegin: ${convo.header.name}`)
|
|
11
11
|
return Promise.resolve()
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
assertConvoStep ({convo, convoStep, args, botMsg}) {
|
|
14
|
+
assertConvoStep ({ convo, convoStep, args, botMsg }) {
|
|
15
15
|
console.log(`MyAsserter assertConvoStep, botMessage: ${utils.inspect(botMsg)} ...`)
|
|
16
16
|
return Promise.resolve()
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
assertConvoEnd ({convo, container, transcript, args}) {
|
|
19
|
+
assertConvoEnd ({ convo, container, transcript, args }) {
|
|
20
20
|
console.log(`MyAsserter assertConvoEnd ${convo.header.name}, transcript: ${utils.inspect(transcript)} ...`)
|
|
21
21
|
return Promise.resolve()
|
|
22
22
|
}
|
package/src/Capabilities.js
CHANGED
|
@@ -3,6 +3,7 @@ module.exports = {
|
|
|
3
3
|
TESTSESSIONNAME: 'TESTSESSIONNAME',
|
|
4
4
|
TESTCASENAME: 'TESTCASENAME',
|
|
5
5
|
TEMPDIR: 'TEMPDIR',
|
|
6
|
+
SAFEDIR: 'SAFEDIR',
|
|
6
7
|
CLEANUPTEMPDIR: 'CLEANUPTEMPDIR',
|
|
7
8
|
WAITFORBOTTIMEOUT: 'WAITFORBOTTIMEOUT',
|
|
8
9
|
CONTAINERMODE: 'CONTAINERMODE',
|
|
@@ -12,7 +13,7 @@ module.exports = {
|
|
|
12
13
|
BOTIUMGRIDURL: 'BOTIUMGRIDURL',
|
|
13
14
|
BOTIUMAPITOKEN: 'BOTIUMAPITOKEN',
|
|
14
15
|
BOTIUMGRIDSLOT: 'BOTIUMGRIDSLOT',
|
|
15
|
-
// Simple
|
|
16
|
+
// Simple Rest Bot Settings
|
|
16
17
|
SIMPLEREST_PING_URL: 'SIMPLEREST_PING_URL',
|
|
17
18
|
SIMPLEREST_PING_VERB: 'SIMPLEREST_PING_VERB',
|
|
18
19
|
SIMPLEREST_PING_BODY: 'SIMPLEREST_PING_BODY',
|
|
@@ -162,4 +162,12 @@ module.exports = class PluginConnectorContainer extends BaseContainer {
|
|
|
162
162
|
return Promise.reject(new Error(`Clean - Botium plugin failed: ${util.inspect(err)}`))
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
|
+
|
|
166
|
+
GetMetaData () {
|
|
167
|
+
try {
|
|
168
|
+
return (this.pluginInstance.GetMetaData ? (this.pluginInstance.GetMetaData() || Promise.resolve()) : Promise.resolve())
|
|
169
|
+
} catch (err) {
|
|
170
|
+
return Promise.reject(new Error(`GetMetaData - Botium plugin failed: ${util.inspect(err)}`))
|
|
171
|
+
}
|
|
172
|
+
}
|
|
165
173
|
}
|
|
@@ -336,24 +336,33 @@ module.exports = class SimpleRestContainer {
|
|
|
336
336
|
|
|
337
337
|
const result = []
|
|
338
338
|
if (isFromUser) {
|
|
339
|
-
const _extractFrom = (root, jsonPaths) => {
|
|
340
|
-
const
|
|
339
|
+
const _extractFrom = (root, jsonPaths, acceptFn = null) => {
|
|
340
|
+
const result = []
|
|
341
341
|
for (const jsonPath of jsonPaths) {
|
|
342
|
+
const jsonPathRes = []
|
|
342
343
|
const rb = jp.query(root, jsonPath)
|
|
343
344
|
if (_.isArray(rb)) {
|
|
344
|
-
_.flattenDeep(rb).forEach(r =>
|
|
345
|
-
} else
|
|
346
|
-
|
|
345
|
+
_.flattenDeep(rb).forEach(r => jsonPathRes.push(r))
|
|
346
|
+
} else {
|
|
347
|
+
jsonPathRes.push(rb)
|
|
348
|
+
}
|
|
349
|
+
if (acceptFn) {
|
|
350
|
+
result.push(...jsonPathRes.filter(r => acceptFn(root, jsonPath, r)))
|
|
351
|
+
} else {
|
|
352
|
+
result.push(...jsonPathRes)
|
|
347
353
|
}
|
|
348
354
|
}
|
|
349
|
-
return
|
|
355
|
+
return result
|
|
350
356
|
}
|
|
351
357
|
|
|
352
358
|
const jsonPathRoots = []
|
|
353
|
-
|
|
354
359
|
const jsonPathsBody = getAllCapValues(Capabilities.SIMPLEREST_BODY_JSONPATH, this.caps)
|
|
355
360
|
if (jsonPathsBody.length > 0) {
|
|
356
|
-
jsonPathRoots.push(..._extractFrom(body, jsonPathsBody)
|
|
361
|
+
jsonPathRoots.push(..._extractFrom(body, jsonPathsBody, (root, jsonPath, r) => {
|
|
362
|
+
if (r && _.isObject(r)) return true
|
|
363
|
+
debug(`Ignoring result body from ${jsonPath} - not a querieable object (${util.inspect(r)})`)
|
|
364
|
+
return false
|
|
365
|
+
}))
|
|
357
366
|
} else {
|
|
358
367
|
jsonPathRoots.push(body)
|
|
359
368
|
}
|
|
@@ -381,7 +390,6 @@ module.exports = class SimpleRestContainer {
|
|
|
381
390
|
const jsonPathsButtonsPayload = getAllCapValues(`${capPrefix}_PAYLOAD_SUBJSONPATH`, this.caps)
|
|
382
391
|
|
|
383
392
|
const retrievedButtons = []
|
|
384
|
-
|
|
385
393
|
const responseButtons = _extractFrom(jsonPathButtonRoot, jsonPathsButtons)
|
|
386
394
|
for (const responseButton of responseButtons) {
|
|
387
395
|
const retrievedButton = {}
|
|
@@ -5,7 +5,6 @@ const debug = require('debug')('botium-connector-PluginConnectorContainer-helper
|
|
|
5
5
|
|
|
6
6
|
const SimpleRestContainer = require('./SimpleRestContainer')
|
|
7
7
|
const Capabilities = require('../../Capabilities')
|
|
8
|
-
const { BotiumError } = require('../../scripting/BotiumError')
|
|
9
8
|
|
|
10
9
|
const pluginResolver = (containermode) => {
|
|
11
10
|
if (containermode === 'simplerest') {
|
|
@@ -59,23 +58,6 @@ const loadConnectorModule = (PluginClass, args) => {
|
|
|
59
58
|
|
|
60
59
|
const tryLoadPlugin = (containermode, modulepath, args) => {
|
|
61
60
|
const pluginLoaderSpec = modulepath || containermode
|
|
62
|
-
const _checkUnsafe = (caps, mode, cause) => {
|
|
63
|
-
if (!caps[Capabilities.SECURITY_ALLOW_UNSAFE]) {
|
|
64
|
-
throw new BotiumError(
|
|
65
|
-
`Security Error. Using unsafe connector mode "${mode}" is not allowed`,
|
|
66
|
-
{
|
|
67
|
-
type: 'security',
|
|
68
|
-
subtype: 'allow unsafe',
|
|
69
|
-
source: 'src/containers/plugins/index.js',
|
|
70
|
-
cause: {
|
|
71
|
-
SECURITY_ALLOW_UNSAFE: caps[Capabilities.SECURITY_ALLOW_UNSAFE],
|
|
72
|
-
mode,
|
|
73
|
-
...cause
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
61
|
|
|
80
62
|
if (pluginResolver(pluginLoaderSpec)) {
|
|
81
63
|
const pluginInstance = new (pluginResolver(pluginLoaderSpec))(args)
|
|
@@ -88,44 +70,50 @@ const tryLoadPlugin = (containermode, modulepath, args) => {
|
|
|
88
70
|
return pluginInstance
|
|
89
71
|
}
|
|
90
72
|
const loadErr = []
|
|
73
|
+
const allowUnsafe = !!args.caps[Capabilities.SECURITY_ALLOW_UNSAFE]
|
|
91
74
|
|
|
92
75
|
if (_.isString(pluginLoaderSpec)) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
76
|
+
if (args.caps.SAFEDIR) {
|
|
77
|
+
const tryLoadFile = path.resolve(args.caps.SAFEDIR, pluginLoaderSpec)
|
|
78
|
+
if (tryLoadFile.startsWith(path.resolve(args.caps.SAFEDIR))) {
|
|
79
|
+
if (fs.existsSync(tryLoadFile)) {
|
|
80
|
+
try {
|
|
81
|
+
let plugin = require(tryLoadFile)
|
|
82
|
+
if (plugin.default) {
|
|
83
|
+
plugin = plugin.default
|
|
84
|
+
}
|
|
85
|
+
if (!plugin.PluginVersion || !plugin.PluginClass) {
|
|
86
|
+
loadErr.push(`Invalid Botium plugin loaded from ${tryLoadFile}, expected PluginVersion, PluginClass fields`)
|
|
87
|
+
} else {
|
|
88
|
+
const pluginInstance = loadConnectorModule(plugin.PluginClass, args)
|
|
89
|
+
debug(`Botium plugin loaded from ${tryLoadFile}`)
|
|
90
|
+
return pluginInstance
|
|
91
|
+
}
|
|
92
|
+
} catch (err) {
|
|
93
|
+
loadErr.push(`Loading Botium plugin from ${tryLoadFile} failed - ${err.message}`)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (allowUnsafe) {
|
|
96
100
|
try {
|
|
97
|
-
let plugin = require(
|
|
101
|
+
let plugin = require(pluginLoaderSpec)
|
|
98
102
|
if (plugin.default) {
|
|
99
103
|
plugin = plugin.default
|
|
100
104
|
}
|
|
101
105
|
if (!plugin.PluginVersion || !plugin.PluginClass) {
|
|
102
|
-
loadErr.push(`Invalid Botium plugin loaded from ${
|
|
106
|
+
loadErr.push(`Invalid Botium plugin loaded from ${pluginLoaderSpec}, expected PluginVersion, PluginClass fields`)
|
|
103
107
|
} else {
|
|
104
108
|
const pluginInstance = loadConnectorModule(plugin.PluginClass, args)
|
|
105
|
-
debug(`Botium plugin loaded from ${
|
|
109
|
+
debug(`Botium plugin loaded from ${pluginLoaderSpec}. Plugin version is ${getModuleVersionSafe(pluginLoaderSpec)}`)
|
|
106
110
|
return pluginInstance
|
|
107
111
|
}
|
|
108
112
|
} catch (err) {
|
|
109
|
-
loadErr.push(`Loading Botium plugin from ${
|
|
113
|
+
loadErr.push(`Loading Botium plugin from ${pluginLoaderSpec} failed - ${err.message}`)
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
116
|
|
|
113
|
-
try {
|
|
114
|
-
let plugin = require(pluginLoaderSpec)
|
|
115
|
-
if (plugin.default) {
|
|
116
|
-
plugin = plugin.default
|
|
117
|
-
}
|
|
118
|
-
if (!plugin.PluginVersion || !plugin.PluginClass) {
|
|
119
|
-
loadErr.push(`Invalid Botium plugin loaded from ${pluginLoaderSpec}, expected PluginVersion, PluginClass fields`)
|
|
120
|
-
} else {
|
|
121
|
-
const pluginInstance = loadConnectorModule(plugin.PluginClass, args)
|
|
122
|
-
debug(`Botium plugin loaded from ${pluginLoaderSpec}. Plugin version is ${getModuleVersionSafe(pluginLoaderSpec)}`)
|
|
123
|
-
return pluginInstance
|
|
124
|
-
}
|
|
125
|
-
} catch (err) {
|
|
126
|
-
loadErr.push(`Loading Botium plugin from ${pluginLoaderSpec} failed - ${err.message}`)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
117
|
const tryLoadPackage = `botium-connector-${pluginLoaderSpec}`
|
|
130
118
|
try {
|
|
131
119
|
let plugin = require(tryLoadPackage)
|
package/src/helpers/HookUtils.js
CHANGED
|
@@ -1,48 +1,28 @@
|
|
|
1
1
|
const util = require('util')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const fs = require('fs')
|
|
4
|
-
const { NodeVM } = require('vm2')
|
|
5
|
-
const esprima = require('esprima')
|
|
6
4
|
const _ = require('lodash')
|
|
7
5
|
const debug = require('debug')('botium-core-HookUtils')
|
|
8
6
|
|
|
9
7
|
const Capabilities = require('../Capabilities')
|
|
10
|
-
const { BotiumError } = require('../scripting/BotiumError')
|
|
11
8
|
|
|
12
|
-
const executeHook = async (caps, hook, args) => {
|
|
13
|
-
return executeHookSync(caps, hook, args)
|
|
9
|
+
const executeHook = async (caps, hook, ...args) => {
|
|
10
|
+
return executeHookSync(caps, hook, ...args)
|
|
14
11
|
}
|
|
15
12
|
|
|
16
|
-
const executeHookSync = (caps, hook, args) => {
|
|
13
|
+
const executeHookSync = (caps, hook, ...args) => {
|
|
17
14
|
if (!hook) {
|
|
18
15
|
return
|
|
19
16
|
}
|
|
20
17
|
|
|
21
18
|
if (_.isFunction(hook)) {
|
|
22
19
|
try {
|
|
23
|
-
return hook(args)
|
|
20
|
+
return hook(...args)
|
|
24
21
|
} catch (err) {
|
|
25
22
|
throw new Error(`Calling Hook function failed: ${err.message}`)
|
|
26
23
|
}
|
|
27
24
|
}
|
|
28
25
|
|
|
29
|
-
if (_.isString(hook)) {
|
|
30
|
-
try {
|
|
31
|
-
const vm = new NodeVM({
|
|
32
|
-
eval: false,
|
|
33
|
-
require: false,
|
|
34
|
-
sandbox: args
|
|
35
|
-
})
|
|
36
|
-
const r = vm.run(hook)
|
|
37
|
-
if (_.isFunction(r)) {
|
|
38
|
-
return r(args)
|
|
39
|
-
} else {
|
|
40
|
-
return r
|
|
41
|
-
}
|
|
42
|
-
} catch (err) {
|
|
43
|
-
throw new Error(`Calling Hook Javascript code failed: ${err.message}`)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
26
|
throw new Error(`Unknown hook ${typeof hook}`)
|
|
47
27
|
}
|
|
48
28
|
|
|
@@ -58,56 +38,40 @@ const getHook = (caps, data) => {
|
|
|
58
38
|
}
|
|
59
39
|
|
|
60
40
|
if (_.isString(data)) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
} catch (err) {
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (resultWithRequire) {
|
|
77
|
-
if (!allowUnsafe) {
|
|
78
|
-
throw new BotiumError(
|
|
79
|
-
'Security Error. Using unsafe custom hook with require is not allowed',
|
|
80
|
-
{
|
|
81
|
-
type: 'security',
|
|
82
|
-
subtype: 'allow unsafe',
|
|
83
|
-
source: path.basename(__filename),
|
|
84
|
-
cause: {
|
|
85
|
-
SECURITY_ALLOW_UNSAFE: caps[Capabilities.SECURITY_ALLOW_UNSAFE],
|
|
86
|
-
hookData: data
|
|
41
|
+
if (caps.SAFEDIR) {
|
|
42
|
+
const tryLoadFile = path.resolve(caps.SAFEDIR, data)
|
|
43
|
+
if (tryLoadFile.startsWith(path.resolve(caps.SAFEDIR))) {
|
|
44
|
+
if (fs.existsSync(tryLoadFile)) {
|
|
45
|
+
try {
|
|
46
|
+
const resultWithRequire = require(tryLoadFile)
|
|
47
|
+
if (_.isFunction(resultWithRequire)) {
|
|
48
|
+
debug(`found hook, type: safedir, in ${tryLoadFile}`)
|
|
49
|
+
return resultWithRequire
|
|
50
|
+
} else {
|
|
51
|
+
throw new Error(`Expected function from hook specification "${util.inspect(data)}", got: "${util.inspect(resultWithRequire)}"`)
|
|
87
52
|
}
|
|
53
|
+
} catch (err) {
|
|
54
|
+
debug(`Failed loading hook, type: safedir, from ${tryLoadFile} failed: ${err.message || err}`)
|
|
88
55
|
}
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (_.isFunction(resultWithRequire)) {
|
|
93
|
-
debug(`found hook, type: require, in ${tryLoadFile}`)
|
|
94
|
-
return resultWithRequire
|
|
95
|
-
} else {
|
|
96
|
-
throw new Error(`Cant load hook ${tryLoadFile} because it is not a function`)
|
|
56
|
+
}
|
|
97
57
|
}
|
|
98
58
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
59
|
+
if (allowUnsafe || data.startsWith('botium-')) {
|
|
60
|
+
const tryLoadFile = data
|
|
61
|
+
try {
|
|
62
|
+
const resultWithRequire = require(tryLoadFile)
|
|
63
|
+
if (_.isFunction(resultWithRequire)) {
|
|
64
|
+
debug(`found hook, type: require, in ${tryLoadFile}`)
|
|
65
|
+
return resultWithRequire
|
|
66
|
+
} else {
|
|
67
|
+
throw new Error(`Expected function from hook specification "${util.inspect(data)}", got: "${util.inspect(resultWithRequire)}"`)
|
|
68
|
+
}
|
|
69
|
+
} catch (err) {
|
|
70
|
+
debug(`Failed loading hook, type: require, from ${tryLoadFile} failed: ${err.message || err}`)
|
|
71
|
+
}
|
|
104
72
|
}
|
|
105
|
-
|
|
106
|
-
debug('Found hook, type: JavaScript as String')
|
|
107
|
-
return data
|
|
108
73
|
}
|
|
109
|
-
|
|
110
|
-
throw new Error(`Not valid hook ${util.inspect(data)}`)
|
|
74
|
+
throw new Error(`Hook specification "${util.inspect(data)}" invalid: no loader available`)
|
|
111
75
|
}
|
|
112
76
|
|
|
113
77
|
module.exports = {
|
|
@@ -3,7 +3,6 @@ const debug = require('debug')('botium-core-ScriptingMemory')
|
|
|
3
3
|
const randomize = require('randomatic')
|
|
4
4
|
const { v1: uuidv1 } = require('uuid')
|
|
5
5
|
const moment = require('moment')
|
|
6
|
-
const { NodeVM } = require('vm2')
|
|
7
6
|
const _ = require('lodash')
|
|
8
7
|
const path = require('path')
|
|
9
8
|
const jp = require('jsonpath')
|
|
@@ -181,29 +180,6 @@ const SCRIPTING_FUNCTIONS_RAW = {
|
|
|
181
180
|
else return ''
|
|
182
181
|
},
|
|
183
182
|
numberOfArguments: 1
|
|
184
|
-
},
|
|
185
|
-
|
|
186
|
-
$func: {
|
|
187
|
-
handler: (caps, code) => {
|
|
188
|
-
if (code == null) {
|
|
189
|
-
throw Error('func function used without args!')
|
|
190
|
-
}
|
|
191
|
-
try {
|
|
192
|
-
const vm = new NodeVM({
|
|
193
|
-
eval: false,
|
|
194
|
-
require: false,
|
|
195
|
-
env: caps[Capabilities.SECURITY_ALLOW_UNSAFE] ? process.env : {},
|
|
196
|
-
sandbox: {
|
|
197
|
-
caps,
|
|
198
|
-
moment
|
|
199
|
-
}
|
|
200
|
-
})
|
|
201
|
-
return vm.run(`module.exports = (${code})`)
|
|
202
|
-
} catch (err) {
|
|
203
|
-
throw Error(`func function execution failed - ${err}`)
|
|
204
|
-
}
|
|
205
|
-
},
|
|
206
|
-
numberOfArguments: 1
|
|
207
183
|
}
|
|
208
184
|
}
|
|
209
185
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
const
|
|
1
|
+
const util = require('util')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const fs = require('fs')
|
|
4
4
|
const isClass = require('is-class')
|
|
5
5
|
const debug = require('debug')('botium-core-asserterUtils')
|
|
6
6
|
|
|
7
|
-
const { BotiumError } = require('../BotiumError')
|
|
8
|
-
|
|
9
7
|
const { DEFAULT_ASSERTERS, DEFAULT_LOGIC_HOOKS, DEFAULT_USER_INPUTS } = require('./LogicHookConsts')
|
|
10
8
|
|
|
11
9
|
DEFAULT_ASSERTERS.forEach((asserter) => {
|
|
@@ -129,19 +127,7 @@ module.exports = class LogicHookUtils {
|
|
|
129
127
|
}
|
|
130
128
|
}
|
|
131
129
|
|
|
132
|
-
const
|
|
133
|
-
if (!this.caps[Capabilities.SECURITY_ALLOW_UNSAFE]) {
|
|
134
|
-
throw new BotiumError(
|
|
135
|
-
'Security Error. Using unsafe component is not allowed',
|
|
136
|
-
{
|
|
137
|
-
type: 'security',
|
|
138
|
-
subtype: 'allow unsafe',
|
|
139
|
-
source: path.basename(__filename),
|
|
140
|
-
cause: { src: !!src, ref, args, hookType }
|
|
141
|
-
}
|
|
142
|
-
)
|
|
143
|
-
}
|
|
144
|
-
}
|
|
130
|
+
const allowUnsafe = !!this.caps[Capabilities.SECURITY_ALLOW_UNSAFE]
|
|
145
131
|
|
|
146
132
|
if (!src) {
|
|
147
133
|
const packageName = `botium-${hookType}-${ref}`
|
|
@@ -154,10 +140,10 @@ module.exports = class LogicHookUtils {
|
|
|
154
140
|
} else if (isClass(CheckClass.PluginClass)) {
|
|
155
141
|
return new CheckClass.PluginClass({ ref, ...this.buildScriptContext }, this.caps, args)
|
|
156
142
|
} else {
|
|
157
|
-
throw new Error(
|
|
143
|
+
throw new Error('Either class or function or PluginClass field expected')
|
|
158
144
|
}
|
|
159
145
|
} catch (err) {
|
|
160
|
-
throw new Error(`
|
|
146
|
+
throw new Error(`Logic Hook specification ${ref} ${hookType} (${packageName}) invalid: ${err.message}`)
|
|
161
147
|
}
|
|
162
148
|
}
|
|
163
149
|
|
|
@@ -166,14 +152,14 @@ module.exports = class LogicHookUtils {
|
|
|
166
152
|
const CheckClass = src
|
|
167
153
|
return new CheckClass({ ref, ...this.buildScriptContext }, this.caps, args)
|
|
168
154
|
} catch (err) {
|
|
169
|
-
throw new Error(`
|
|
155
|
+
throw new Error(`Logic Hook specification ${ref} from class invalid: ${err.message}`)
|
|
170
156
|
}
|
|
171
157
|
}
|
|
172
158
|
if (_.isFunction(src)) {
|
|
173
159
|
try {
|
|
174
160
|
return src({ ref, ...this.buildScriptContext }, this.caps, args)
|
|
175
161
|
} catch (err) {
|
|
176
|
-
throw new Error(`
|
|
162
|
+
throw new Error(`Logic Hook specification ${ref} from function invalid: ${err.message}`)
|
|
177
163
|
}
|
|
178
164
|
}
|
|
179
165
|
if (_.isObject(src) && !_.isString(src)) {
|
|
@@ -183,26 +169,15 @@ module.exports = class LogicHookUtils {
|
|
|
183
169
|
const script = src[key]
|
|
184
170
|
if (_.isFunction(script)) {
|
|
185
171
|
return script(args)
|
|
186
|
-
} else if (_.isString(script)) {
|
|
187
|
-
try {
|
|
188
|
-
const vm = new NodeVM({
|
|
189
|
-
eval: false,
|
|
190
|
-
require: false,
|
|
191
|
-
sandbox: args
|
|
192
|
-
})
|
|
193
|
-
return vm.run(script)
|
|
194
|
-
} catch (err) {
|
|
195
|
-
throw new Error(`Script ${key} is not valid - ${err.message || err}`)
|
|
196
|
-
}
|
|
197
172
|
} else {
|
|
198
|
-
throw new Error(`Script
|
|
173
|
+
throw new Error(`Script ${key} is not valid - only functions accepted`)
|
|
199
174
|
}
|
|
200
175
|
}
|
|
201
176
|
return result
|
|
202
177
|
}, {})
|
|
203
178
|
return hookObject
|
|
204
179
|
} catch (err) {
|
|
205
|
-
throw new Error(`
|
|
180
|
+
throw new Error(`Logic Hook specification ${ref} ${hookType} from provided src (${util.inspect(src)}) invalid: ${err.message}`)
|
|
206
181
|
}
|
|
207
182
|
}
|
|
208
183
|
|
|
@@ -215,8 +190,8 @@ module.exports = class LogicHookUtils {
|
|
|
215
190
|
}]
|
|
216
191
|
if (src.indexOf('/') >= 0) {
|
|
217
192
|
tryLoads.push({
|
|
218
|
-
tryLoadPackageName: src.
|
|
219
|
-
tryLoadAsserterByName: src.
|
|
193
|
+
tryLoadPackageName: src.substring(0, src.lastIndexOf('/')),
|
|
194
|
+
tryLoadAsserterByName: src.substring(src.lastIndexOf('/') + 1)
|
|
220
195
|
})
|
|
221
196
|
}
|
|
222
197
|
|
|
@@ -243,29 +218,34 @@ module.exports = class LogicHookUtils {
|
|
|
243
218
|
} else if (_.isFunction(CheckClass.PluginClass)) {
|
|
244
219
|
return CheckClass.PluginClass({ ref, ...this.buildScriptContext }, this.caps, args)
|
|
245
220
|
} else {
|
|
246
|
-
throw new Error(
|
|
221
|
+
throw new Error('Expected class or function')
|
|
247
222
|
}
|
|
248
223
|
}
|
|
249
224
|
|
|
250
225
|
for (const tryLoad of tryLoads) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
226
|
+
if (this.caps.SAFEDIR) {
|
|
227
|
+
const tryLoadFile = path.resolve(this.caps.SAFEDIR, tryLoad.tryLoadPackageName)
|
|
228
|
+
if (tryLoadFile.startsWith(path.resolve(this.caps.SAFEDIR))) {
|
|
229
|
+
if (fs.existsSync(tryLoadFile)) {
|
|
230
|
+
try {
|
|
231
|
+
return tryLoadFromSource(tryLoadFile, tryLoad.tryLoadAsserterByName)
|
|
232
|
+
} catch (err) {
|
|
233
|
+
loadErr.push(`Logic Hook specification ${ref} ${hookType} from "${src}" invalid: ${err.message} `)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (allowUnsafe || tryLoad.tryLoadPackageName.startsWith('botium-')) {
|
|
254
239
|
try {
|
|
255
|
-
return tryLoadFromSource(
|
|
240
|
+
return tryLoadFromSource(tryLoad.tryLoadPackageName, tryLoad.tryLoadAsserterByName)
|
|
256
241
|
} catch (err) {
|
|
257
|
-
loadErr.push(`
|
|
242
|
+
loadErr.push(`Logic Hook specification ${ref} ${hookType} from "${src}" invalid: ${err.message} `)
|
|
258
243
|
}
|
|
259
244
|
}
|
|
260
|
-
try {
|
|
261
|
-
return tryLoadFromSource(tryLoad.tryLoadPackageName, tryLoad.tryLoadAsserterByName)
|
|
262
|
-
} catch (err) {
|
|
263
|
-
loadErr.push(`Failed to fetch ${ref} ${hookType} from ${src} - ${err.message} `)
|
|
264
|
-
}
|
|
265
245
|
}
|
|
266
246
|
|
|
267
247
|
loadErr.forEach(debug)
|
|
268
248
|
}
|
|
269
|
-
throw new Error(`
|
|
249
|
+
throw new Error(`Logic Hook specification ${ref} ${hookType} from "${util.inspect(src)}" invalid : no loader available`)
|
|
270
250
|
}
|
|
271
251
|
}
|