mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-29 12:22:44 +08:00
vm 即将在渲染进程内被禁用,改用简单的沙箱执行命令
This commit is contained in:
parent
24d0dd1e2a
commit
a27fe4c338
53
plugin/lib/nodeFns.js
Normal file
53
plugin/lib/nodeFns.js
Normal file
@ -0,0 +1,53 @@
|
||||
const nodeFns = {
|
||||
AbortController,
|
||||
AbortSignal,
|
||||
Array,
|
||||
ArrayBuffer,
|
||||
Boolean,
|
||||
Buffer,
|
||||
DataView,
|
||||
Date,
|
||||
Error,
|
||||
EvalError,
|
||||
Float32Array,
|
||||
Float64Array,
|
||||
Function,
|
||||
Infinity,
|
||||
Int16Array,
|
||||
Int32Array,
|
||||
Int8Array,
|
||||
Intl,
|
||||
JSON,
|
||||
Math,
|
||||
NaN,
|
||||
Number,
|
||||
Object,
|
||||
RangeError,
|
||||
ReferenceError,
|
||||
RegExp,
|
||||
String,
|
||||
SyntaxError,
|
||||
TypeError,
|
||||
URIError,
|
||||
URL,
|
||||
URLSearchParams,
|
||||
Uint16Array,
|
||||
Uint32Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
atob,
|
||||
btoa,
|
||||
decodeURI,
|
||||
decodeURIComponent,
|
||||
encodeURI,
|
||||
encodeURIComponent,
|
||||
escape,
|
||||
isFinite,
|
||||
isNaN,
|
||||
parseFloat,
|
||||
parseInt,
|
||||
require,
|
||||
unescape,
|
||||
};
|
||||
|
||||
module.exports = nodeFns
|
@ -1,3 +0,0 @@
|
||||
if (parseInt(process.versions.node.split('.')[0]) < 6) throw new Error('vm2 requires Node.js version 6 or newer.');
|
||||
|
||||
module.exports = require('./lib/main');
|
@ -1,35 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const pa = require('path');
|
||||
|
||||
const {NodeVM, VMError} = require('../');
|
||||
|
||||
if (process.argv[2]) {
|
||||
const path = pa.resolve(process.argv[2]);
|
||||
|
||||
console.log(`\x1B[90m[vm] creating VM for ${path}\x1B[39m`);
|
||||
const started = Date.now();
|
||||
|
||||
try {
|
||||
NodeVM.file(path, {
|
||||
verbose: true,
|
||||
require: {
|
||||
external: true
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`\x1B[90m[vm] VM completed in ${Date.now() - started}ms\x1B[39m`);
|
||||
} catch (ex) {
|
||||
if (ex instanceof VMError) {
|
||||
console.error(`\x1B[31m[vm:error] ${ex.message}\x1B[39m`);
|
||||
} else {
|
||||
const {stack} = ex;
|
||||
|
||||
if (stack) {
|
||||
console.error(`\x1B[31m[vm:error] ${stack}\x1B[39m`);
|
||||
} else {
|
||||
console.error(`\x1B[31m[vm:error] ${ex}\x1B[39m`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,83 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// eslint-disable-next-line no-invalid-this, no-shadow
|
||||
const {GeneratorFunction, AsyncFunction, AsyncGeneratorFunction, global, internal, host, hook} = this;
|
||||
const {Contextify, Decontextify} = internal;
|
||||
// eslint-disable-next-line no-shadow
|
||||
const {Function, eval: eval_, Promise, Object, Reflect} = global;
|
||||
const {getOwnPropertyDescriptor, defineProperty, assign} = Object;
|
||||
const {apply: rApply, construct: rConstruct} = Reflect;
|
||||
|
||||
const FunctionHandler = {
|
||||
__proto__: null,
|
||||
apply(target, thiz, args) {
|
||||
const type = this.type;
|
||||
args = Decontextify.arguments(args);
|
||||
try {
|
||||
args = Contextify.value(hook(type, args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
return rApply(target, thiz, args);
|
||||
},
|
||||
construct(target, args, newTarget) {
|
||||
const type = this.type;
|
||||
args = Decontextify.arguments(args);
|
||||
try {
|
||||
args = Contextify.value(hook(type, args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
return rConstruct(target, args, newTarget);
|
||||
}
|
||||
};
|
||||
|
||||
function makeCheckFunction(type) {
|
||||
return assign({
|
||||
__proto__: null,
|
||||
type
|
||||
}, FunctionHandler);
|
||||
}
|
||||
|
||||
function override(obj, prop, value) {
|
||||
const desc = getOwnPropertyDescriptor(obj, prop);
|
||||
desc.value = value;
|
||||
defineProperty(obj, prop, desc);
|
||||
}
|
||||
|
||||
const proxiedFunction = new host.Proxy(Function, makeCheckFunction('function'));
|
||||
override(Function.prototype, 'constructor', proxiedFunction);
|
||||
if (GeneratorFunction) {
|
||||
Object.setPrototypeOf(GeneratorFunction, proxiedFunction);
|
||||
override(GeneratorFunction.prototype, 'constructor', new host.Proxy(GeneratorFunction, makeCheckFunction('generator_function')));
|
||||
}
|
||||
if (AsyncFunction) {
|
||||
Object.setPrototypeOf(AsyncFunction, proxiedFunction);
|
||||
override(AsyncFunction.prototype, 'constructor', new host.Proxy(AsyncFunction, makeCheckFunction('async_function')));
|
||||
}
|
||||
if (AsyncGeneratorFunction) {
|
||||
Object.setPrototypeOf(AsyncGeneratorFunction, proxiedFunction);
|
||||
override(AsyncGeneratorFunction.prototype, 'constructor', new host.Proxy(AsyncGeneratorFunction, makeCheckFunction('async_generator_function')));
|
||||
}
|
||||
|
||||
global.Function = proxiedFunction;
|
||||
global.eval = new host.Proxy(eval_, makeCheckFunction('eval'));
|
||||
|
||||
if (Promise) {
|
||||
|
||||
Promise.prototype.then = new host.Proxy(Promise.prototype.then, makeCheckFunction('promise_then'));
|
||||
// This seems not to work, and will produce
|
||||
// UnhandledPromiseRejectionWarning: TypeError: Method Promise.prototype.then called on incompatible receiver [object Object].
|
||||
// This is likely caused since the host.Promise.prototype.then cannot use the VM Proxy object.
|
||||
// Contextify.connect(host.Promise.prototype.then, Promise.prototype.then);
|
||||
|
||||
if (Promise.prototype.finally) {
|
||||
Promise.prototype.finally = new host.Proxy(Promise.prototype.finally, makeCheckFunction('promise_finally'));
|
||||
// Contextify.connect(host.Promise.prototype.finally, Promise.prototype.finally);
|
||||
}
|
||||
if (Promise.prototype.catch) {
|
||||
Promise.prototype.catch = new host.Proxy(Promise.prototype.catch, makeCheckFunction('promise_catch'));
|
||||
// Contextify.connect(host.Promise.prototype.catch, Promise.prototype.catch);
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
// source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
|
||||
function escapeRegExp(string) {
|
||||
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||
}
|
||||
|
||||
function match(wildcard, s) {
|
||||
const regexString = escapeRegExp(wildcard).replace(/\\\*/g, '\\S*').replace(/\\\?/g, '.');
|
||||
const regex = new RegExp(regexString);
|
||||
return regex.test(s);
|
||||
}
|
||||
|
||||
module.exports = {match};
|
File diff suppressed because it is too large
Load Diff
@ -1,682 +0,0 @@
|
||||
/* eslint-disable no-shadow, no-invalid-this */
|
||||
/* global vm, host, Contextify, Decontextify, VMError, options */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {Script} = host.require('vm');
|
||||
const fs = host.require('fs');
|
||||
const pa = host.require('path');
|
||||
|
||||
const BUILTIN_MODULES = host.process.binding('natives');
|
||||
const parseJSON = JSON.parse;
|
||||
const importModuleDynamically = () => {
|
||||
// We can't throw an error object here because since vm.Script doesn't store a context, we can't properly contextify that error object.
|
||||
// eslint-disable-next-line no-throw-literal
|
||||
throw 'Dynamic imports are not allowed.';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Object} host Hosts's internal objects.
|
||||
*/
|
||||
|
||||
return ((vm, host) => {
|
||||
'use strict';
|
||||
|
||||
const global = this;
|
||||
|
||||
const TIMERS = new host.WeakMap(); // Contains map of timers created inside sandbox
|
||||
const BUILTINS = {__proto__: null};
|
||||
const CACHE = {__proto__: null};
|
||||
const EXTENSIONS = {
|
||||
__proto__: null,
|
||||
['.json'](module, filename) {
|
||||
try {
|
||||
const code = fs.readFileSync(filename, 'utf8');
|
||||
module.exports = parseJSON(code);
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
['.node'](module, filename) {
|
||||
if (vm.options.require.context === 'sandbox') throw new VMError('Native modules can be required only with context set to \'host\'.');
|
||||
|
||||
try {
|
||||
module.exports = Contextify.readonly(host.require(filename));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (let i = 0; i < vm.options.sourceExtensions.length; i++) {
|
||||
const ext = vm.options.sourceExtensions[i];
|
||||
|
||||
EXTENSIONS['.' + ext] = (module, filename, dirname) => {
|
||||
if (vm.options.require.context !== 'sandbox') {
|
||||
try {
|
||||
module.exports = Contextify.readonly(host.require(filename));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
} else {
|
||||
let script;
|
||||
|
||||
try {
|
||||
// Load module
|
||||
let contents = fs.readFileSync(filename, 'utf8');
|
||||
contents = vm._compiler(contents, filename);
|
||||
|
||||
const code = host.STRICT_MODULE_PREFIX + contents + host.MODULE_SUFFIX;
|
||||
|
||||
const ccode = vm._hook('run', [code]);
|
||||
|
||||
// Precompile script
|
||||
script = new Script(ccode, {
|
||||
__proto__: null,
|
||||
filename: filename || 'vm.js',
|
||||
displayErrors: false,
|
||||
importModuleDynamically
|
||||
});
|
||||
|
||||
} catch (ex) {
|
||||
throw Contextify.value(ex);
|
||||
}
|
||||
|
||||
const closure = script.runInContext(global, {
|
||||
__proto__: null,
|
||||
filename: filename || 'vm.js',
|
||||
displayErrors: false,
|
||||
importModuleDynamically
|
||||
});
|
||||
|
||||
// run the script
|
||||
closure(module.exports, module.require, module, filename, dirname);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const _parseExternalOptions = (options) => {
|
||||
if (host.Array.isArray(options)) {
|
||||
return {
|
||||
__proto__: null,
|
||||
external: options,
|
||||
transitive: false
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
__proto__: null,
|
||||
external: options.modules,
|
||||
transitive: options.transitive
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve filename.
|
||||
*/
|
||||
|
||||
const _resolveFilename = (path) => {
|
||||
if (!path) return null;
|
||||
let hasPackageJson;
|
||||
try {
|
||||
path = pa.resolve(path);
|
||||
|
||||
const exists = fs.existsSync(path);
|
||||
const isdir = exists ? fs.statSync(path).isDirectory() : false;
|
||||
|
||||
// direct file match
|
||||
if (exists && !isdir) return path;
|
||||
|
||||
// load as file
|
||||
|
||||
for (let i = 0; i < vm.options.sourceExtensions.length; i++) {
|
||||
const ext = vm.options.sourceExtensions[i];
|
||||
if (fs.existsSync(`${path}.${ext}`)) return `${path}.${ext}`;
|
||||
}
|
||||
if (fs.existsSync(`${path}.json`)) return `${path}.json`;
|
||||
if (fs.existsSync(`${path}.node`)) return `${path}.node`;
|
||||
|
||||
// load as module
|
||||
|
||||
hasPackageJson = fs.existsSync(`${path}/package.json`);
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
if (hasPackageJson) {
|
||||
let pkg;
|
||||
try {
|
||||
pkg = fs.readFileSync(`${path}/package.json`, 'utf8');
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
try {
|
||||
pkg = parseJSON(pkg);
|
||||
} catch (ex) {
|
||||
throw new VMError(`Module '${path}' has invalid package.json`, 'EMODULEINVALID');
|
||||
}
|
||||
|
||||
let main;
|
||||
if (pkg && pkg.main) {
|
||||
main = _resolveFilename(`${path}/${pkg.main}`);
|
||||
if (!main) main = _resolveFilename(`${path}/index`);
|
||||
} else {
|
||||
main = _resolveFilename(`${path}/index`);
|
||||
}
|
||||
|
||||
return main;
|
||||
}
|
||||
|
||||
// load as directory
|
||||
|
||||
try {
|
||||
for (let i = 0; i < vm.options.sourceExtensions.length; i++) {
|
||||
const ext = vm.options.sourceExtensions[i];
|
||||
if (fs.existsSync(`${path}/index.${ext}`)) return `${path}/index.${ext}`;
|
||||
}
|
||||
|
||||
if (fs.existsSync(`${path}/index.json`)) return `${path}/index.json`;
|
||||
if (fs.existsSync(`${path}/index.node`)) return `${path}/index.node`;
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Builtin require.
|
||||
*/
|
||||
|
||||
const _requireBuiltin = (moduleName) => {
|
||||
if (moduleName === 'buffer') return ({Buffer});
|
||||
if (BUILTINS[moduleName]) return BUILTINS[moduleName].exports; // Only compiled builtins are stored here
|
||||
|
||||
if (moduleName === 'util') {
|
||||
return Contextify.readonly(host.require(moduleName), {
|
||||
// Allows VM context to use util.inherits
|
||||
__proto__: null,
|
||||
inherits: (ctor, superCtor) => {
|
||||
ctor.super_ = superCtor;
|
||||
Object.setPrototypeOf(ctor.prototype, superCtor.prototype);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (moduleName === 'events' || moduleName === 'internal/errors') {
|
||||
let script;
|
||||
try {
|
||||
script = new Script(`(function (exports, require, module, process, internalBinding) {
|
||||
'use strict';
|
||||
const primordials = global;
|
||||
${BUILTIN_MODULES[moduleName]}
|
||||
\n
|
||||
});`, {
|
||||
filename: `${moduleName}.vm.js`
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
// setup module scope
|
||||
const module = BUILTINS[moduleName] = {
|
||||
exports: {},
|
||||
require: _requireBuiltin
|
||||
};
|
||||
|
||||
// run script
|
||||
try {
|
||||
// FIXME binding should be contextified
|
||||
script.runInContext(global)(module.exports, module.require, module, host.process, host.process.binding);
|
||||
} catch (e) {
|
||||
// e could be from inside or outside of sandbox
|
||||
throw new VMError(`Error loading '${moduleName}'`);
|
||||
}
|
||||
return module.exports;
|
||||
}
|
||||
|
||||
return Contextify.readonly(host.require(moduleName));
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepare require.
|
||||
*/
|
||||
|
||||
const _prepareRequire = (currentDirname, parentAllowsTransitive = false) => {
|
||||
const _require = moduleName => {
|
||||
let requireObj;
|
||||
try {
|
||||
const optionsObj = vm.options;
|
||||
if (optionsObj.nesting && moduleName === 'vm2') return {VM: Contextify.readonly(host.VM), NodeVM: Contextify.readonly(host.NodeVM)};
|
||||
requireObj = optionsObj.require;
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
if (!requireObj) throw new VMError(`Access denied to require '${moduleName}'`, 'EDENIED');
|
||||
if (moduleName == null) throw new VMError("Module '' not found.", 'ENOTFOUND');
|
||||
if (typeof moduleName !== 'string') throw new VMError(`Invalid module name '${moduleName}'`, 'EINVALIDNAME');
|
||||
|
||||
let filename;
|
||||
let allowRequireTransitive = false;
|
||||
|
||||
// Mock?
|
||||
|
||||
try {
|
||||
const {mock} = requireObj;
|
||||
if (mock) {
|
||||
const mockModule = mock[moduleName];
|
||||
if (mockModule) {
|
||||
return Contextify.readonly(mockModule);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
// Builtin?
|
||||
|
||||
if (BUILTIN_MODULES[moduleName]) {
|
||||
let allowed;
|
||||
try {
|
||||
const builtinObj = requireObj.builtin;
|
||||
if (host.Array.isArray(builtinObj)) {
|
||||
if (builtinObj.indexOf('*') >= 0) {
|
||||
allowed = builtinObj.indexOf(`-${moduleName}`) === -1;
|
||||
} else {
|
||||
allowed = builtinObj.indexOf(moduleName) >= 0;
|
||||
}
|
||||
} else if (builtinObj) {
|
||||
allowed = builtinObj[moduleName];
|
||||
} else {
|
||||
allowed = false;
|
||||
}
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
if (!allowed) throw new VMError(`Access denied to require '${moduleName}'`, 'EDENIED');
|
||||
|
||||
return _requireBuiltin(moduleName);
|
||||
}
|
||||
|
||||
// External?
|
||||
|
||||
let externalObj;
|
||||
try {
|
||||
externalObj = requireObj.external;
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
if (!externalObj) throw new VMError(`Access denied to require '${moduleName}'`, 'EDENIED');
|
||||
|
||||
if (/^(\.|\.\/|\.\.\/)/.exec(moduleName)) {
|
||||
// Module is relative file, e.g. ./script.js or ../script.js
|
||||
|
||||
if (!currentDirname) throw new VMError('You must specify script path to load relative modules.', 'ENOPATH');
|
||||
|
||||
filename = _resolveFilename(`${currentDirname}/${moduleName}`);
|
||||
} else if (/^(\/|\\|[a-zA-Z]:\\)/.exec(moduleName)) {
|
||||
// Module is absolute file, e.g. /script.js or //server/script.js or C:\script.js
|
||||
|
||||
filename = _resolveFilename(moduleName);
|
||||
} else {
|
||||
// Check node_modules in path
|
||||
|
||||
if (!currentDirname) throw new VMError('You must specify script path to load relative modules.', 'ENOPATH');
|
||||
|
||||
if (typeof externalObj === 'object') {
|
||||
let isWhitelisted;
|
||||
try {
|
||||
const { external, transitive } = _parseExternalOptions(externalObj);
|
||||
|
||||
isWhitelisted = external.some(ext => host.helpers.match(ext, moduleName)) || (transitive && parentAllowsTransitive);
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
if (!isWhitelisted) {
|
||||
throw new VMError(`The module '${moduleName}' is not whitelisted in VM.`, 'EDENIED');
|
||||
}
|
||||
|
||||
allowRequireTransitive = true;
|
||||
}
|
||||
|
||||
// FIXME the paths array has side effects
|
||||
const paths = currentDirname.split(pa.sep);
|
||||
|
||||
while (paths.length) {
|
||||
const path = paths.join(pa.sep);
|
||||
|
||||
// console.log moduleName, "#{path}#{pa.sep}node_modules#{pa.sep}#{moduleName}"
|
||||
|
||||
filename = _resolveFilename(`${path}${pa.sep}node_modules${pa.sep}${moduleName}`);
|
||||
if (filename) break;
|
||||
|
||||
paths.pop();
|
||||
}
|
||||
}
|
||||
|
||||
if (!filename) {
|
||||
let resolveFunc;
|
||||
try {
|
||||
resolveFunc = requireObj.resolve;
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
if (resolveFunc) {
|
||||
let resolved;
|
||||
try {
|
||||
resolved = requireObj.resolve(moduleName, currentDirname);
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
filename = _resolveFilename(resolved);
|
||||
}
|
||||
}
|
||||
if (!filename) throw new VMError(`Cannot find module '${moduleName}'`, 'ENOTFOUND');
|
||||
|
||||
// return cache whenever possible
|
||||
if (CACHE[filename]) return CACHE[filename].exports;
|
||||
|
||||
const dirname = pa.dirname(filename);
|
||||
const extname = pa.extname(filename);
|
||||
|
||||
let allowedModule = true;
|
||||
try {
|
||||
const rootObj = requireObj.root;
|
||||
if (rootObj) {
|
||||
const rootPaths = host.Array.isArray(rootObj) ? rootObj : host.Array.of(rootObj);
|
||||
allowedModule = rootPaths.some(path => host.String.prototype.startsWith.call(dirname, pa.resolve(path)));
|
||||
}
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
if (!allowedModule) {
|
||||
throw new VMError(`Module '${moduleName}' is not allowed to be required. The path is outside the border!`, 'EDENIED');
|
||||
}
|
||||
|
||||
const module = CACHE[filename] = {
|
||||
filename,
|
||||
exports: {},
|
||||
require: _prepareRequire(dirname, allowRequireTransitive)
|
||||
};
|
||||
|
||||
// lookup extensions
|
||||
if (EXTENSIONS[extname]) {
|
||||
EXTENSIONS[extname](module, filename, dirname);
|
||||
return module.exports;
|
||||
}
|
||||
|
||||
throw new VMError(`Failed to load '${moduleName}': Unknown type.`, 'ELOADFAIL');
|
||||
};
|
||||
|
||||
return _require;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepare sandbox.
|
||||
*/
|
||||
|
||||
// This is a function and not an arrow function, since the original is also a function
|
||||
global.setTimeout = function setTimeout(callback, delay, ...args) {
|
||||
if (typeof callback !== 'function') throw new TypeError('"callback" argument must be a function');
|
||||
let tmr;
|
||||
try {
|
||||
tmr = host.setTimeout(Decontextify.value(() => {
|
||||
// FIXME ...args has side effects
|
||||
callback(...args);
|
||||
}), Decontextify.value(delay));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
const local = Contextify.value(tmr);
|
||||
|
||||
TIMERS.set(local, tmr);
|
||||
return local;
|
||||
};
|
||||
|
||||
global.setInterval = function setInterval(callback, interval, ...args) {
|
||||
if (typeof callback !== 'function') throw new TypeError('"callback" argument must be a function');
|
||||
let tmr;
|
||||
try {
|
||||
tmr = host.setInterval(Decontextify.value(() => {
|
||||
// FIXME ...args has side effects
|
||||
callback(...args);
|
||||
}), Decontextify.value(interval));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
const local = Contextify.value(tmr);
|
||||
|
||||
TIMERS.set(local, tmr);
|
||||
return local;
|
||||
};
|
||||
|
||||
global.setImmediate = function setImmediate(callback, ...args) {
|
||||
if (typeof callback !== 'function') throw new TypeError('"callback" argument must be a function');
|
||||
let tmr;
|
||||
try {
|
||||
tmr = host.setImmediate(Decontextify.value(() => {
|
||||
// FIXME ...args has side effects
|
||||
callback(...args);
|
||||
}));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
const local = Contextify.value(tmr);
|
||||
|
||||
TIMERS.set(local, tmr);
|
||||
return local;
|
||||
};
|
||||
|
||||
global.clearTimeout = function clearTimeout(local) {
|
||||
try {
|
||||
host.clearTimeout(TIMERS.get(local));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
};
|
||||
|
||||
global.clearInterval = function clearInterval(local) {
|
||||
try {
|
||||
host.clearInterval(TIMERS.get(local));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
};
|
||||
|
||||
global.clearImmediate = function clearImmediate(local) {
|
||||
try {
|
||||
host.clearImmediate(TIMERS.get(local));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
};
|
||||
|
||||
function addListener(name, handler) {
|
||||
if (name !== 'beforeExit' && name !== 'exit') {
|
||||
throw new Error(`Access denied to listen for '${name}' event.`);
|
||||
}
|
||||
|
||||
try {
|
||||
host.process.on(name, Decontextify.value(handler));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
const {argv: optionArgv, env: optionsEnv} = options;
|
||||
|
||||
// FIXME wrong class structure
|
||||
global.process = {
|
||||
argv: optionArgv !== undefined ? Contextify.value(optionArgv) : [],
|
||||
title: host.process.title,
|
||||
version: host.process.version,
|
||||
versions: Contextify.readonly(host.process.versions),
|
||||
arch: host.process.arch,
|
||||
platform: host.process.platform,
|
||||
env: optionsEnv !== undefined ? Contextify.value(optionsEnv) : {},
|
||||
pid: host.process.pid,
|
||||
features: Contextify.readonly(host.process.features),
|
||||
nextTick: function nextTick(callback, ...args) {
|
||||
if (typeof callback !== 'function') {
|
||||
throw new Error('Callback must be a function.');
|
||||
}
|
||||
|
||||
try {
|
||||
host.process.nextTick(Decontextify.value(() => {
|
||||
// FIXME ...args has side effects
|
||||
callback(...args);
|
||||
}));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
hrtime: function hrtime(time) {
|
||||
try {
|
||||
return Contextify.value(host.process.hrtime(Decontextify.value(time)));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
cwd: function cwd() {
|
||||
try {
|
||||
return Contextify.value(host.process.cwd());
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
addListener,
|
||||
on: addListener,
|
||||
|
||||
once: function once(name, handler) {
|
||||
if (name !== 'beforeExit' && name !== 'exit') {
|
||||
throw new Error(`Access denied to listen for '${name}' event.`);
|
||||
}
|
||||
|
||||
try {
|
||||
host.process.once(name, Decontextify.value(handler));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
listeners: function listeners(name) {
|
||||
if (name !== 'beforeExit' && name !== 'exit') {
|
||||
// Maybe add ({__proto__:null})[name] to throw when name fails in https://tc39.es/ecma262/#sec-topropertykey.
|
||||
return [];
|
||||
}
|
||||
|
||||
// Filter out listeners, which were not created in this sandbox
|
||||
try {
|
||||
return Contextify.value(host.process.listeners(name).filter(listener => Contextify.isVMProxy(listener)));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
|
||||
removeListener: function removeListener(name, handler) {
|
||||
if (name !== 'beforeExit' && name !== 'exit') {
|
||||
return this;
|
||||
}
|
||||
|
||||
try {
|
||||
host.process.removeListener(name, Decontextify.value(handler));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
umask: function umask() {
|
||||
if (arguments.length) {
|
||||
throw new Error('Access denied to set umask.');
|
||||
}
|
||||
|
||||
try {
|
||||
return Contextify.value(host.process.umask());
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (vm.options.console === 'inherit') {
|
||||
global.console = Contextify.readonly(host.console);
|
||||
} else if (vm.options.console === 'redirect') {
|
||||
global.console = {
|
||||
debug(...args) {
|
||||
try {
|
||||
// FIXME ...args has side effects
|
||||
vm.emit('console.debug', ...Decontextify.arguments(args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
log(...args) {
|
||||
try {
|
||||
// FIXME ...args has side effects
|
||||
vm.emit('console.log', ...Decontextify.arguments(args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
info(...args) {
|
||||
try {
|
||||
// FIXME ...args has side effects
|
||||
vm.emit('console.info', ...Decontextify.arguments(args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
warn(...args) {
|
||||
try {
|
||||
// FIXME ...args has side effects
|
||||
vm.emit('console.warn', ...Decontextify.arguments(args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
error(...args) {
|
||||
try {
|
||||
// FIXME ...args has side effects
|
||||
vm.emit('console.error', ...Decontextify.arguments(args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
dir(...args) {
|
||||
try {
|
||||
vm.emit('console.dir', ...Decontextify.arguments(args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
time() {},
|
||||
timeEnd() {},
|
||||
trace(...args) {
|
||||
try {
|
||||
// FIXME ...args has side effects
|
||||
vm.emit('console.trace', ...Decontextify.arguments(args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Return contextified require.
|
||||
*/
|
||||
|
||||
return _prepareRequire;
|
||||
})(vm, host);
|
@ -3,15 +3,11 @@ const os = require('os');
|
||||
const child_process = require("child_process")
|
||||
const iconv = require('iconv-lite')
|
||||
const electron = require('electron')
|
||||
const {
|
||||
NodeVM,
|
||||
VM
|
||||
} = require('./lib/vm2')
|
||||
const path = require("path")
|
||||
const axios = require('axios');
|
||||
const http = require('http');
|
||||
const url = require('url')
|
||||
|
||||
const nodeFns = require("./lib/nodeFns")
|
||||
window._ = require("lodash")
|
||||
window.yuQueClient = axios.create({
|
||||
baseURL: 'https://www.yuque.com/api/v2/',
|
||||
@ -26,8 +22,6 @@ window.yuQueClient = axios.create({
|
||||
|
||||
if (!utools.isWindows()) process.env.PATH += ':/usr/local/bin:/usr/local/sbin'
|
||||
|
||||
// window.startTime = new Date().getTime()
|
||||
|
||||
const shortCodes = [
|
||||
|
||||
open = path => {
|
||||
@ -199,7 +193,6 @@ window.temporaryStoreSoldOut = () => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// python -c
|
||||
window.runPythonCommand = py => {
|
||||
try {
|
||||
@ -280,7 +273,6 @@ window.hexEncode = text => Buffer.from(text, 'utf8').toString('hex')
|
||||
window.hexDecode = text => Buffer.from(text, 'hex').toString('utf8')
|
||||
window.base64Decode = text => Buffer.from(text, 'base64').toString('utf8')
|
||||
|
||||
|
||||
window.processPlatform = process.platform
|
||||
window.joinPath = path.join
|
||||
|
||||
@ -401,6 +393,15 @@ window.getSelectFile = hwnd => {
|
||||
}
|
||||
}
|
||||
|
||||
window.showHelpPage = path => {
|
||||
utools.ubrowser
|
||||
.goto("https://www.yuque.com/fofolee-awga0/cpbg1m/bg31vl" + path)
|
||||
.run({
|
||||
width: 1380,
|
||||
height: 750
|
||||
});
|
||||
}
|
||||
|
||||
window.clipboardReadText = () => electron.clipboard.readText()
|
||||
|
||||
window.convertFilePathToUtoolsPayload = files => files.map(file => {
|
||||
@ -480,64 +481,58 @@ let getSandboxFuns = () => {
|
||||
shortCodes.forEach(f => {
|
||||
sandbox[f.name] = f
|
||||
})
|
||||
Object.assign(sandbox, nodeFns)
|
||||
return sandbox
|
||||
}
|
||||
|
||||
let createNodeVM = (userVars) => {
|
||||
var sandbox = getSandboxFuns()
|
||||
Object.assign(userVars, sandbox)
|
||||
const vm = new NodeVM({
|
||||
require: {
|
||||
external: true,
|
||||
builtin: ["*"],
|
||||
},
|
||||
console: 'redirect',
|
||||
env: process.env,
|
||||
sandbox: userVars,
|
||||
// 简化报错信息
|
||||
let liteErr = e => {
|
||||
if (!e) return
|
||||
return e.error ? e.error.stack.replace(/([ ] +at.+)|(.+\.js:\d+)/g, '').trim() : e.message
|
||||
}
|
||||
|
||||
// vm 模块将无法在渲染进程中使用,改用简单的沙箱来执行代码
|
||||
let createSandbox = (code, sandbox, async = false) => {
|
||||
if (!async) code = `return (${code})`
|
||||
const sandFn = new Function('sandbox', `with(sandbox){${code}}`);
|
||||
const proxy = new Proxy(sandbox, {
|
||||
has(target, key) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return vm
|
||||
return sandFn(proxy);
|
||||
}
|
||||
|
||||
window.showHelpPage = path => {
|
||||
utools.ubrowser
|
||||
.goto("https://www.yuque.com/fofolee-awga0/cpbg1m/bg31vl" + path)
|
||||
.run({
|
||||
width: 1380,
|
||||
height: 750
|
||||
});
|
||||
window.evalCodeInSandbox = (code, userVars = {}) => {
|
||||
let sandbox = getSandboxFuns()
|
||||
let sandboxWithUV = Object.assign(userVars, sandbox)
|
||||
try {
|
||||
return createSandbox(code, sandboxWithUV);
|
||||
} catch (error) {
|
||||
throw liteErr(error)
|
||||
}
|
||||
}
|
||||
|
||||
window.VmEval = (cmd, sandbox = {}) => new VM({
|
||||
sandbox: sandbox
|
||||
}).run(cmd)
|
||||
|
||||
let isWatchingError = false
|
||||
// The vm module of Node.js is deprecated in the renderer process and will be removed
|
||||
window.runCodeInVm = (cmd, callback, userVars = {}) => {
|
||||
const vm = createNodeVM(userVars)
|
||||
//重定向 console
|
||||
vm.on('console.log', (...stdout) => {
|
||||
console.log(stdout);
|
||||
callback(parseStdout(stdout), null)
|
||||
});
|
||||
|
||||
vm.on('console.error', stderr => {
|
||||
callback(null, stderr.toString())
|
||||
});
|
||||
|
||||
let liteErr = e => {
|
||||
if (!e) return
|
||||
return e.error ? e.error.stack.replace(/([ ] +at.+)|(.+\.js:\d+)/g, '').trim() : e.message
|
||||
window.runCodeInSandbox = (code, callback, userVars = {}) => {
|
||||
let sandbox = getSandboxFuns()
|
||||
sandbox.console = {
|
||||
log: (...stdout) => {
|
||||
console.log(stdout);
|
||||
callback(parseStdout(stdout), null)
|
||||
},
|
||||
error: (...stderr) => {
|
||||
callback(null, parseStdout(stderr))
|
||||
}
|
||||
}
|
||||
|
||||
// 错误处理
|
||||
let sandboxWithUV = Object.assign(userVars, sandbox)
|
||||
try {
|
||||
vm.run(cmd, path.join(__dirname, 'preload.js'));
|
||||
createSandbox(code, sandboxWithUV, true)
|
||||
} catch (e) {
|
||||
console.log('Error: ', e)
|
||||
callback(null, liteErr(e))
|
||||
}
|
||||
|
||||
// 自动捕捉错误
|
||||
let cbUnhandledError = e => {
|
||||
removeAllListener()
|
||||
console.log('UnhandledError: ', e)
|
||||
@ -619,7 +614,7 @@ let httpServer
|
||||
window.quickcommandHttpServer = () => {
|
||||
let run = (cmd = '', port = 33442) => {
|
||||
let httpResponse = (res, code, result) => {
|
||||
// 因为无法判断 vm2 是否执行完毕,故只收受一次 console.log,接收后就关闭连接
|
||||
// 只收受一次 console.log,接收后就关闭连接
|
||||
if (res.finished) return
|
||||
res.writeHead(code, {
|
||||
'Content-Type': 'text/html'
|
||||
@ -630,7 +625,7 @@ window.quickcommandHttpServer = () => {
|
||||
let runUserCode = (res, cmd, userVars) => {
|
||||
// 不需要返回输出的提前关闭连接
|
||||
if (!cmd.includes('console.log')) httpResponse(res, 200)
|
||||
window.runCodeInVm(cmd, (stdout, stderr) => {
|
||||
window.runCodeInSandbox(cmd, (stdout, stderr) => {
|
||||
// 错误返回 500
|
||||
if (stderr) return httpResponse(res, 500, stderr)
|
||||
return httpResponse(res, 200, stdout)
|
||||
|
@ -97,7 +97,7 @@ export default {
|
||||
utools.outPlugin();
|
||||
}, 500);
|
||||
if (currentCommand.program === "quickcommand") {
|
||||
window.runCodeInVm(currentCommand.cmd, (stdout, stderr) => {
|
||||
window.runCodeInSandbox(currentCommand.cmd, (stdout, stderr) => {
|
||||
if (stderr) {
|
||||
return quitBeforeShowResult
|
||||
? alert(stderr)
|
||||
|
@ -10,21 +10,21 @@ let escapeItem = item => {
|
||||
|
||||
let handlingJsonVar = (jsonVar, name) => {
|
||||
try {
|
||||
return escapeItem(window.VmEval(jsonVar.slice(2, -2), {
|
||||
return escapeItem(window.evalCodeInSandbox(jsonVar.slice(2, -2), {
|
||||
[name]: quickcommand.enterData.payload
|
||||
}))
|
||||
} catch {
|
||||
return ""
|
||||
} catch (e) {
|
||||
return utools.showNotification(e)
|
||||
}
|
||||
}
|
||||
|
||||
let handlingJsExpression = js => {
|
||||
try {
|
||||
return window.VmEval(js.slice(5, -2), {
|
||||
return window.evalCodeInSandbox(js.slice(5, -2), {
|
||||
utools: window.getuToolsLite(),
|
||||
})
|
||||
} catch {
|
||||
return ""
|
||||
} catch (e) {
|
||||
return utools.showNotification(e)
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +131,7 @@ const specialVars = {
|
||||
name: "js",
|
||||
label: "{{js:}}",
|
||||
desc: "获取js表达式的值,如{{js:utools.isMacOs()}}",
|
||||
tooltip: "注意,必须为表达式而非语句,类似Vue的文本插值。不支持异步/Node",
|
||||
tooltip: "注意,必须为表达式而非语句,类似Vue的文本插值",
|
||||
type: "command",
|
||||
match: /{{js:(.*?)}}/mg,
|
||||
repl: js => handlingJsExpression(js)
|
||||
@ -147,4 +147,4 @@ const specialVars = {
|
||||
}
|
||||
}
|
||||
|
||||
export default specialVars
|
||||
export default specialVars
|
||||
|
13
src/plugins/monaco/types/common.d.ts
vendored
13
src/plugins/monaco/types/common.d.ts
vendored
@ -88,16 +88,3 @@ declare var console: {
|
||||
log(message?: any): void,
|
||||
error(message?: any): void
|
||||
}
|
||||
|
||||
// process
|
||||
declare var process: {
|
||||
argv: any[];
|
||||
title: string;
|
||||
version: string;
|
||||
versions: object;
|
||||
arch: string;
|
||||
platform: string;
|
||||
env: object;
|
||||
pid: number;
|
||||
features: object
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user