fix(core.gbapp): Context in VM is isolated now.

This commit is contained in:
Rodrigo Rodriguez 2020-08-29 15:02:19 -03:00
parent 4cb9d5b906
commit 42a7074081
4 changed files with 45 additions and 47 deletions

View file

@ -72,7 +72,6 @@ export class AdminDialog extends IGBDialog {
const importer = new GBImporter(min.core); const importer = new GBImporter(min.core);
const deployer = new GBDeployer(min.core, importer); const deployer = new GBDeployer(min.core, importer);
const adminService = new GBAdminService(min.core);
AdminDialog.setupSecurityDialogs(min); AdminDialog.setupSecurityDialogs(min);

View file

@ -70,6 +70,8 @@ import { WhatsappDirectLine } from '../../whatsapp.gblib/services/WhatsappDirect
import fs = require('fs'); import fs = require('fs');
import { GuaribasConversationMessage } from '../../analytics.gblib/models'; import { GuaribasConversationMessage } from '../../analytics.gblib/models';
import { GBCoreService } from './GBCoreService'; import { GBCoreService } from './GBCoreService';
import { DialogClass } from './GBAPIService';
import { GBVMService } from './GBVMService';
/** /**
* Minimal service layer for a bot. * Minimal service layer for a bot.
@ -712,7 +714,7 @@ export class GBMinService {
if (hasBadWord) { if (hasBadWord) {
await step.beginDialog('/pleaseNoBadWords'); await step.beginDialog('/pleaseNoBadWords');
} else if (isVMCall) { } else if (isVMCall) {
await GBMinService.callVM(context.activity.text, min, step); await GBVMService.callVM(context.activity.text, min, step, this.deployer);
} else if (context.activity.text.charAt(0) === '/') { } else if (context.activity.text.charAt(0) === '/') {
let text = context.activity.text; let text = context.activity.text;
let parts = text.split(' '); let parts = text.split(' ');
@ -803,9 +805,4 @@ export class GBMinService {
} }
} }
public static async callVM(text: string, min: GBMinInstance, step: GBDialogStep) {
const mainMethod = text.toLowerCase();
min.sandBoxMap[mainMethod][mainMethod].bind(min.sandBoxMap[mainMethod]);
return await min.sandBoxMap[mainMethod][mainMethod](step);
}
} }

View file

@ -33,18 +33,18 @@
'use strict'; 'use strict';
import { WaterfallDialog } from 'botbuilder-dialogs'; import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBLog, GBMinInstance, GBService, IGBCoreService } from 'botlib'; import { GBLog, GBMinInstance, GBService, IGBCoreService, GBDialogStep } from 'botlib';
import * as fs from 'fs'; import * as fs from 'fs';
import { GBDeployer } from './GBDeployer'; import { GBDeployer } from './GBDeployer';
import { TSCompiler } from './TSCompiler'; import { TSCompiler } from './TSCompiler';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
const walkPromise = require('walk-promise'); const walkPromise = require('walk-promise');
const vm = require('vm');
import urlJoin = require('url-join'); import urlJoin = require('url-join');
import { DialogClass } from './GBAPIService'; import { DialogClass } from './GBAPIService';
import { Messages } from '../strings'; import { Messages } from '../strings';
import { GBConversationalService } from './GBConversationalService'; import { GBConversationalService } from './GBConversationalService';
//tslint:disable-next-line:no-submodule-imports //tslint:disable-next-line:no-submodule-imports
const vm = require('vm');
const vb2ts = require('vbscript-to-typescript/dist/converter'); const vb2ts = require('vbscript-to-typescript/dist/converter');
const beautify = require('js-beautify').js; const beautify = require('js-beautify').js;
var textract = require('textract'); var textract = require('textract');
@ -62,15 +62,12 @@ var textract = require('textract');
* Basic services for BASIC manipulation. * Basic services for BASIC manipulation.
*/ */
export class GBVMService extends GBService { export class GBVMService extends GBService {
private readonly script = new vm.Script();
public async loadDialogPackage(folder: string, min: GBMinInstance, core: IGBCoreService, deployer: GBDeployer) { public async loadDialogPackage(folder: string, min: GBMinInstance, core: IGBCoreService, deployer: GBDeployer) {
const files = await walkPromise(folder); const files = await walkPromise(folder);
this.addHearDialog(min); this.addHearDialog(min);
await CollectionUtil.asyncForEach(files, async file => { await CollectionUtil.asyncForEach(files, async file => {
if (!file) { if (!file) {
return; return;
} }
@ -85,12 +82,11 @@ export class GBVMService extends GBService {
let writeVBS = true; let writeVBS = true;
if (fs.existsSync(fullVbsFile)) { if (fs.existsSync(fullVbsFile)) {
const vbsStat = fs.statSync(fullVbsFile); const vbsStat = fs.statSync(fullVbsFile);
if (docxStat.mtimeMs < (vbsStat.mtimeMs + interval)) { if (docxStat.mtimeMs < vbsStat.mtimeMs + interval) {
writeVBS = false; writeVBS = false;
} }
} }
if (writeVBS) { if (writeVBS) {
let text = await this.getTextFromWord(folder, wordFile); let text = await this.getTextFromWord(folder, wordFile);
fs.writeFileSync(urlJoin(folder, vbsFile), text); fs.writeFileSync(urlJoin(folder, vbsFile), text);
} }
@ -113,39 +109,33 @@ export class GBVMService extends GBService {
if (fs.existsSync(jsfile)) { if (fs.existsSync(jsfile)) {
const jsStat = fs.statSync(jsfile); const jsStat = fs.statSync(jsfile);
const interval = 30000; // If compiled is older 30 seconds, then recompile. const interval = 30000; // If compiled is older 30 seconds, then recompile.
if (compiledAt.isFile() && compiledAt.mtimeMs > (jsStat.mtimeMs + interval)) { if (compiledAt.isFile() && compiledAt.mtimeMs > jsStat.mtimeMs + interval) {
await this.executeBASIC(fullFilename, min, deployer, mainName); await this.executeBASIC(fullFilename, min, deployer, mainName);
} } else {
else {
const parsedCode: string = fs.readFileSync(jsfile, 'utf8'); const parsedCode: string = fs.readFileSync(jsfile, 'utf8');
this.executeJS(min, deployer, parsedCode, mainName); this.executeJS(min, deployer, parsedCode, mainName);
} }
} } else {
else {
await this.executeBASIC(fullFilename, min, deployer, mainName); await this.executeBASIC(fullFilename, min, deployer, mainName);
} }
} }
}); });
} }
private async getTextFromWord(folder: string, filename: string) { private async getTextFromWord(folder: string, filename: string) {
return new Promise<string>(async (resolve, reject) => { return new Promise<string>(async (resolve, reject) => {
textract.fromFileWithPath(urlJoin(folder, filename), { preserveLineBreaks: true }, textract.fromFileWithPath(urlJoin(folder, filename), { preserveLineBreaks: true }, (error, text) => {
(error, text) => { if (error) {
if (error) { reject(error);
reject(error); } else {
} text = text.replace('“', '"');
else { text = text.replace('”', '"');
text = text.replace('“', '\"'); text = text.replace('', "'");
text = text.replace('”', '\"'); text = text.replace('', "'");
text = text.replace('', '\'');
text = text.replace('', '\''); resolve(text);
}
});
resolve(text);
}
});
}); });
} }
@ -262,8 +252,8 @@ export class GBVMService extends GBService {
parsedCode = code.substring(pos, pos + match1.index); parsedCode = code.substring(pos, pos + match1.index);
parsedCode += ``; parsedCode += ``;
parsedCode += `const ${promiseName}= async (step, ${variable}) => {` parsedCode += `const ${promiseName}= async (step, ${variable}) => {`;
parsedCode += ` return new Promise(async (resolve) => {` parsedCode += ` return new Promise(async (resolve) => {`;
// Skips old construction and point to the async block. // Skips old construction and point to the async block.
@ -306,7 +296,7 @@ export class GBVMService extends GBService {
parsedCode = this.handleThisAndAwait(parsedCode); parsedCode = this.handleThisAndAwait(parsedCode);
parsedCode = beautify(parsedCode, { indent_size: 2, space_in_empty_paren: true }) parsedCode = beautify(parsedCode, { indent_size: 2, space_in_empty_paren: true });
fs.writeFileSync(jsfile, parsedCode); fs.writeFileSync(jsfile, parsedCode);
this.executeJS(min, deployer, parsedCode, mainName); this.executeJS(min, deployer, parsedCode, mainName);
@ -316,12 +306,8 @@ export class GBVMService extends GBService {
private executeJS(min: GBMinInstance, deployer: GBDeployer, parsedCode: string, mainName: string) { private executeJS(min: GBMinInstance, deployer: GBDeployer, parsedCode: string, mainName: string) {
try { try {
const sandbox: DialogClass = new DialogClass(min, deployer); min.sandBoxMap[mainName.toLowerCase()] = parsedCode;
const context = vm.createContext(sandbox); } catch (error) {
vm.runInContext(parsedCode, context);
min.sandBoxMap[mainName.toLowerCase()] = sandbox;
}
catch (error) {
GBLog.error(`[GBVMService] ERROR loading ${error}`); GBLog.error(`[GBVMService] ERROR loading ${error}`);
} }
} }
@ -346,7 +332,6 @@ export class GBVMService extends GBService {
return $1 === undefined ? 'this.sendFile' : $1; return $1 === undefined ? 'this.sendFile' : $1;
}); });
// await insertion. // await insertion.
code = code.replace(/this\./gm, 'await this.'); code = code.replace(/this\./gm, 'await this.');
@ -385,4 +370,15 @@ export class GBVMService extends GBService {
]) ])
); );
} }
public static async callVM(text: string, min: GBMinInstance, step: GBDialogStep, deployer: GBDeployer) {
const sandbox: DialogClass = new DialogClass(min, deployer);
const context = vm.createContext(sandbox);
const code = min.sandBoxMap[text];
vm.runInContext(code, context);
const mainMethod = text.toLowerCase();
sandbox[mainMethod].bind(sandbox);
return await sandbox[mainMethod](step);
}
} }

View file

@ -43,9 +43,11 @@ import { GBLog, GBMinInstance, IGBDialog, IGBPackage } from 'botlib';
import { Messages } from '../strings'; import { Messages } from '../strings';
import { KBService } from './../services/KBService'; import { KBService } from './../services/KBService';
import { GuaribasAnswer } from '../models'; import { GuaribasAnswer } from '../models';
import { GBMinService } from '../../../packages/core.gbapp/services/GBMinService';
import { SecService } from '../../security.gbapp/services/SecService'; import { SecService } from '../../security.gbapp/services/SecService';
import { CollectionUtil, AzureText } from 'pragmatismo-io-framework'; import { CollectionUtil, AzureText } from 'pragmatismo-io-framework';
import { GBVMService } from '../../core.gbapp/services/GBVMService';
import { GBImporter } from '../../core.gbapp/services/GBImporterService';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
/** /**
* Dialog arguments. * Dialog arguments.
@ -59,6 +61,7 @@ export class AskDialogArgs {
* Handle the ask loop on knowledge base data or delegate to other services. * Handle the ask loop on knowledge base data or delegate to other services.
*/ */
export class AskDialog extends IGBDialog { export class AskDialog extends IGBDialog {
static deployer: any;
/** /**
* Setup dialogs flows and define services call. * Setup dialogs flows and define services call.
* *
@ -67,6 +70,9 @@ export class AskDialog extends IGBDialog {
*/ */
public static setup(bot: BotAdapter, min: GBMinInstance) { public static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new KBService(min.core.sequelize); const service = new KBService(min.core.sequelize);
const importer = new GBImporter(min.core);
this.deployer = new GBDeployer(min.core, importer);
min.dialogs.add(new WaterfallDialog('/answerEvent', AskDialog.getAnswerEventDialog(service, min))); min.dialogs.add(new WaterfallDialog('/answerEvent', AskDialog.getAnswerEventDialog(service, min)));
min.dialogs.add(new WaterfallDialog('/answer', AskDialog.getAnswerDialog(min, service))); min.dialogs.add(new WaterfallDialog('/answer', AskDialog.getAnswerDialog(min, service)));
min.dialogs.add(new WaterfallDialog('/ask', AskDialog.getAskDialog(min))); min.dialogs.add(new WaterfallDialog('/ask', AskDialog.getAskDialog(min)));
@ -259,7 +265,7 @@ export class AskDialog extends IGBDialog {
private static async handleAnswer(service: KBService, min: GBMinInstance, step: any, answer: GuaribasAnswer) { private static async handleAnswer(service: KBService, min: GBMinInstance, step: any, answer: GuaribasAnswer) {
if (answer.content.endsWith('.docx')) { if (answer.content.endsWith('.docx')) {
const mainName = answer.content.replace(/\s|\-/gi, '').split('.')[0]; const mainName = answer.content.replace(/\s|\-/gi, '').split('.')[0];
return await GBMinService.callVM(mainName, min, step); return await GBVMService.callVM(mainName, min, step, this.deployer);
} else { } else {
await service.sendAnswer(min, AskDialog.getChannel(step), step, answer); await service.sendAnswer(min, AskDialog.getChannel(step), step, answer);
return await step.replaceDialog('/ask', { isReturning: true }); return await step.replaceDialog('/ask', { isReturning: true });