Giter Site home page Giter Site logo

sablejs's Introduction

LOGO

linux ci osx ci windows ci Version

🎉 sablejs 2.0 will be opening all the code, please click to learn more about our milestone and goals.

English | 简体中文

The safer and faster ECMA5.1 interpreter written by JavaScript, it can be used:

  1. Sandbox (like Figma Plugin Sandbox, but better and easier to use);
  2. Protect JavaScript source code via AOT compiling to opcode.

sablejs covered ~95% test262 es5-tests cases, it can be safely used in production.

Quick Start

sablejs includes the Compiler and Interpreter independently, so we removed the related dynamic api from the spec (see Limits 1). In short, you need to compile your JavaScript code with sablejs cli before you run it.

Example

Suppose we have the following code in fib.js:

function fib(n) {
  return n < 2 ? n : fib(n - 1) + fib(n - 2);
}

var start = Date.now();
console.log("[INFO] fib: " + fib(30));
console.log("[INFO] time consuming: " + (Date.now() - start) + "ms");

Compile It!

> npm i sablejs -g
> sablejs -i fib.js -o output # get output file that contains base64 string

sablejs cli includes the following commands:

Usage: sablejs [options]

Options:
  -v, --vers           output the current version
  -i, --input <path>   compile input filepath
  -o, --output <path>  compile output filepath
  -j  --json           don't do Base64 compress, output simple json result
  -s, --slient         don't output log
  -h, --help

Run It!

> npm install sablejs --save

or you can import to your html directly

<script src="https://cdn.jsdelivr.net/npm/[email protected]/runtime.js"></script>
Browser
const VM = require("sablejs/runtime")();

// import console.log function to vm call
const vm = new VM();
const vGlobal = vm.getGlobal();
const vConsole = vm.createObject();
const vLog = vm.createFunction("log", function () {
  const temp = [];
  for (let i = 0; i < arguments.length; i++) {
    temp.push(vm.asString(arguments[i]));
  }

  console.log(...temp);
  return vm.createUndefined();
});

vm.setProperty(vConsole, "log", vLog);
vm.setProperty(vGlobal, "console", vConsole);

(async () => {
  const resp = await fetch("<output url>");
  const data = await resp.text();
  vm.run(data);
  vm.destroy();
})();
Node
const VM = require("sablejs/runtime")();
const fs = require("fs");

// import console.log function to vm call
const vm = new VM();
const vGlobal = vm.getGlobal();
const vConsole = vm.createObject();
const vLog = vm.createFunction("log", function () {
  const temp = [];
  for (let i = 0; i < arguments.length; i++) {
    temp.push(vm.asString(arguments[i]));
  }

  console.log(...temp);
  return vm.createUndefined();
});

vm.setProperty(vConsole, "log", vLog);
vm.setProperty(vGlobal, "console", vConsole);

// please run: sablejs -i fib.js -o output
vm.run(fs.readFileSync("./output").toString());
vm.destroy();

APIs

  • VM.prototype.run(source, isSimpleJSON)
    • source: String - the compiled result via sablejs compiler
    • isSimpleJSON: Boolean - if be true, you should use -j to make compiler output simple json result, default false.
    • return: undefined

Initialize the VM and execute the compiled source code.

const VM = require('sablejs/runtime')();
const vm = new VM();

// source should be base64 string via sablejs compiling
vm.run(`<compiled source string>`);
  • VM.prototype.getGlobal()
    • return: Value

Returns the global in the VM, which is similar to the window in browser and the global in Node.js.

const global = vm.getGlobal();
  • VM.prototype.createUndefined()
    • return Value

Create an undefined boxed type.

const vUndefined = vm.createUndefined();
  • VM.prototype.createNull()
    • return: Value

Create an null boxed type.

const vNull = vm.createNull();
  • VM.prototype.createBoolean(bool)
    • bool: Boolean
    • return Value

Create an bool boxed type.

const vBoolean = vm.createBoolean(true);
  • VM.prototype.createNumber(num)
    • num: Number
    • return Value

Create an number boxed type.

const vNumber = vm.createNumber(1024);
  • VM.prototype.createString(str)
    • str: String
    • return Value

Create an string boxed type.

const vString = vm.createString('Hello World!');
  • VM.prototype.createObject()
    • return Value

Create an object boxed type.

const vObject = vm.createObject();
  • VM.prototype.createArray(length)
    • length: Number | undefined
    • return Value

Create an array boxed type.

const vArray1 = vm.createArray();
// or
const vArray2 = vm.createArray(128);
  • VM.prototype.createFunction(name, func)
    • name: String
    • func: Function
    • return Value

Create an funcntion boxed type. It receives a function name and the specific implementation of the function. Both the function parameter and this are boxed types in func.

const vFunction = vm.createFunction("trim", function(str) {
  // this is the undefined or new's instannce boxed type
  // str maybe the string boxed type, we need to check it
});
  • VM.prototype.createError(message)
    • message: String | undefined
    • return Value

Create an error boxed type.

const vError1 = vm.createError();
// or
const vError2 = vm.createError("unknown error");
  • VM.prototype.createRegExp(pattern, flags)
    • pattern: String
    • flags: String | undefined
    • return Value

Create an regexp boxed type.

const vRegExp = vm.createRegExp("\\w+", "ig");
  • VM.prototype.createDate()
    • return Value

Create an date boxed type.

const vDate = vm.createDate();
  • VM.prototype.isUndefined(value)
    • value: Value
    • return Boolean

Used to determine if the type is undefinend.

const vUndefined = vm.createUndefined();
if(vm.isUndefined(vUndefined)) {
  // ...
}
  • VM.prototype.isNull(value)
    • value: Value
    • return Boolean

Used to determine if the type is null.

const vNull = vm.createNull();
if(vm.isNull(vNull)) {
  // ...
}
  • VM.prototype.isBoolean(value)
    • value: Value
    • return Boolean

Used to determine if the type is bool.

const vBoolean = vm.createBoolean(true);
if(vm.isBoolean(vBoolean)) {
  // ...
}
  • VM.prototype.isNumber(value)
    • value: Value
    • return Boolean

Used to determine if the type is number.

const vNumber = vm.createNumber(1024);
if(vm.isNumber(vNumber)) {
  // ...
}
  • VM.prototype.isString(value)
    • value: Value
    • return Boolean

Used to determine if the type is string.

const vString = vm.createString("Hello World!");
if(vm.isString(vString)) {
  // ...
}
  • VM.prototype.isObject(value)
    • value: Value
    • return Boolean

Used to determine if the type is object.

const vObject = vm.createObject();
const vArray = vm.createArray();
if(vm.isObject(vObject) && vm.isObject(vArray)) {
  // ...
}
  • VM.prototype.isArray(value)
    • value: Value
    • return Boolean

Used to determine if the type is array.

const vArray = vm.createArray();
if(vm.isArray(vArray)) {
  // ...
}
  • VM.prototype.isFunction(value)
    • value: Value
    • return Boolean

Used to determine if the type is function.

const vFunction = vm.createFunction("log", function(){});
if(vm.isFunction(vFunction)){
  // ...
}
  • VM.prototype.isError(value)
    • value: Value
    • return Boolean

Used to determine if the type is error.

const vError = vm.createError('unknown error');
if(vm.isError(vError)){
  // ...
}
  • VM.prototype.isRegExp(value)
    • value: Value
    • return Boolean

Used to determine if the type is regexp.

const vRegExp = vm.createRegExp("\\w+", "ig");
if(vm.isRegExp(vRegExp)){
  // ...
}
  • VM.prototype.isDate(value)
    • value: Value
    • return Boolean

Used to determine if the type is date.

const vDate = vm.createDate();
if(vm.isDate(vDate)){
  // ...
}
  • VM.prototype.asUndefined(value)
    • value: Value
    • return undefined

Converting undefined boxed type to plain undefined value.

const vUndefined = vm.createUndefined();
vm.asUndefined(vUndefined) === undefined;
  • VM.prototype.asNull(value)
    • value: Value
    • return null

Converting null boxed type to plain null value.

const vNull = vm.createNull();
vm.asNull(vNull) === null;
  • VM.prototype.asBoolean(value)
    • value: Value
    • return Boolean

Converting bool boxed type to plain bool value.

const vBoolean = vm.createBoolean(true);
const boolean = vm.asBoolean(vBoolean);
if(boolean === true) {
  // ...
}
  • VM.prototype.asNumber(value)
    • value: Value
    • return Number

Converting number boxed type to plain number value.

const vNumber = vm.createNumber(1024);
const number = vm.asNumber(vNumber);
if(number === 1024) {
  // ...
}
  • VM.prototype.asString(value)
    • value: Value
    • return String

Converting string boxed type to plain string value.

const vString = vm.createString('Hello World!');
const string = vm.asString(vString);
if(string === 'Hello World!') {
  // ...
}
  • VM.prototype.asObject(value)
    • value: Value
    • return Object

Converting object boxed type to inner object value.

const vObject = vm.createFunction("asObject", function(){});
const object = vm.asObject(vObject);
if(object.type === 12) {
  // ...
}
  • VM.prototype.instanceof(lval, rval)
    • lval: Value
    • rval: Value
    • return Boolean

Equivalent to the instanceof keyword.

const global = vm.getGlobal();
const vDateFunc = vm.getProperty(global, "Date");
const vDate = vm.createDate();
if(vm.instanceof(vDate, vDateFunc)) {
  // ...
}
  • VM.prototype.typeof(value)
    • value: Value
    • return String

Equivalent to the typeof keyword.

const vString = vm.createString('Hello World!');
if(vm.typeof(vString) === "string") {
  // ...
}
  • VM.prototype.getProperty(value, name)
    • value: Value
    • name: String
    • return Value

Get the value of the property of the object. Return is a property boxed type.

const global = vm.getGlobal();
const vPrint = vm.getProperty(global, "print");
if(vm.isFunction(vPrint)) {
  // ...
}
  • VM.prototype.setProperty(value, name, property)
    • value: Value
    • name: String
    • property: Value
    • return Value

Assigning the property to object. Return is a property boxed type.

const global = vm.getGlobal();
const console = vm.createObject();
const log = vm.createFunction("log", function() {
  // console.log impl
});

vm.setProperty(console, "log", log);
vm.setProperty(global, "console", console);
  • VM.prototype.deleteProperty(value, name)
    • value: Value
    • name: String
    • return Boolean

Delete the property of object.

const global = vm.getGlobal();
vm.deleteProperty(global, "print");

const vPrint = vm.getProperty(global, "print");
if(vm.isUndefined(vPrint)) {
  // ...
} 
  • VM.prototype.defineProperty(value, name, desc)
    • value: Value
    • name: String
    • desc: Object
    • return Value

Equivalent to the Object.defineProperty function.

const vObject = vm.createObject();
vm.defineProperty(vObject, "name", { 
  value: vm.createString("sablejs"),
});

const getter = vm.createFunction("getter", function() {
  return vm.createNumber("101");
});

const setter = vm.createFunction("setter", function(age) {
  vm.setProperty(this, "__age__", age);
});

vm.defineProperty(vObject, "age", {
  enumerable: false,
  get: getter,
  set: setter,
});
  • VM.prototype.getPrototype(value)
    • value: Value
    • return Value

Get the prototype of object.

const global = vm.getGlobal();
const vStringFunc = vm.getProperty(global, "String");
if(!vm.isUndefined(vStringFunc)) {
  const vTrimStart = vm.createFunction("trimStart", function() {
    const str = vm.asString(this);
    return vm.createString(str);
  });

  const vStringFuncProto = vm.getPrototype(vStringFunc);
  vm.setProperty(vStringFuncProto, "trimStart", vTrimStart);
}
  • VM.prototype.setPrototype(value, prototype)
    • value: Value
    • prototype: Value
    • return Value

Set the prototype of object.

const vA = vm.createFunction("A", function() {});
const vObject = vm.createObject();

vm.setProperty(vObject, 'name', vm.createString('Hello World!'));
vm.setPrototype(vA, vObject);
  • VM.prototype.throw(value)
    • value: Value
    • return undefined

Equivalent to the throw keyword.

const vError = vm.createError('unknown error');
vm.throw(vError);
  • VM.prototype.new(func[, arg1, arg2, arg3...])
    • func: Value
    • arg: Value
    • return Value

Equivalent to the new keyword.

const vA = vm.createFunction('A', function(name) {
  vm.setProperty(this, 'name', name);
});

vm.new(vA, vm.createString("A"));
  • VM.prototype.call(func, thisPtr[, arg1, arg2, arg3...])
    • func: Value
    • thisPtr: Value | undefined
    • arg: Value
    • return Value

Equivalent to the Function.prototype.call function.

const vLog = vm.createFunction('log', function() {
  const temp = [];
  for(let i = 0; i < arguments.length; i++){
    temp.push(vm.asString(arguments[i]));
  }
  console.log(...temp); // '1', 1, false
});

vm.call(
  vLog, 
  vm.createUndefined(), 
  vm.createString('1'), 
  vm.createNumber(1), 
  vm.createBoolean(false)
);
  • VM.prototype.destroy
    • return undefined

Destroy the VM instance.

vm.destroy();

Benchmark

sablejs may be the fastest interpreter written in JavaScript (using v8 benchmark suites):

Benchmark Enviorment:

  • Node.js v12.19.0
  • Golang 1.15.6
  • GCC 5.4.0 -O3
  • 2.4 GHz Intel Core i9
  • MacOS Mojave 10.14.6 (18G6032)
sablejs sval eval5 quickjs-wasm goja
Language JavaScript JavaScript JavaScript C + WebAssembly Golang
Richards 110 24.9 24.7 376 208
Crypto 114 24.6 20.2 400 104
RayTrace 258 92.2 98.5 471 294
NavierStokes 183 35.9 49.8 665 191
DeltaBlue 120 35.3 29.5 402 276
Total score 148 37.3 37.3 452 202
Baseline 1 ▼ 2.96 ▼ 2.96 ▲ 2.05 ▲ 0.36
File Size(KB) 216 152 134 434 -
Gzip Size(KB) 29 40 34 245 -

Limits

  1. Dynamic execution by eval and Function is forbidden, but passing literal string/number/null and undefined is allowed (the interpreter doesn't contain any compiler).
eval("print('Hello World!')"); // it's ok
eval("var " + "a=1"); // it's ok

var str = "Hello World!";
eval("print('" + str + "')"); // throw SyntaxError

Function("a", "b", "return a+b"); // it's ok
new Function("a", "b", "return a+b"); // it's ok

var str = "return a+b";
Function("a", "b", str); // throw SyntaxError
new Function("a", "b", str); // throw SyntaxError
  1. The browser environment relies on native browser functions such as btoa / unescape / decodeURIComponent, etc. if you need support for IE9 or below, you need to add shims.

Bindings

License

sablejs JavaScript Engine

Copyright (c) 2020-Now ErosZhao

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

Non-profit projects of individuals or organizations and commercial projects with commercial authorization of the author.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

sablejs's People

Contributors

eroszy avatar georgezouq avatar richardo2016 avatar xiejiss avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sablejs's Issues

如何调用js里的方法

大佬 你好
假设我的js如下
function sign(data) {
//进行签名算法
return data;
}
编译后我如何通vm调用这个sign算法并传参呢

关于在sablejs处理后的JS中使用document,window对象的问题

首先非常感谢您的耐心解答,已经解决了问题。但又遇到个问题,就是当丢到vm中的脚本里包含document,window对象时,会报not defined的错误,想问下,是不支持这些对象么?然后包含jquery的脚本也会报错。如果不行的话,是否有办法在外部这个脚本中调用vm中脚本里的函数呢?其实就是想实现浏览器和服务端传输数据的加密,又不想暴露加密思路。感谢感谢!

`vm.run(data)`: console is not defined

//test.js
console.log(1)

Then I run sablejs -i test.js -o output

//main.js
const fs = require('fs');
const data = fs.readFileSync("output").toString();
const VM = require('sablejs/runtime')();
const vm = new VM();
vm.run(data);
vm.destroy();

Then I run node main.js,output is as follows:

test % node main.js
test/node_modules/sablejs/runtime.js:1
(function (exports, require, module, __filename, __dirname) { 'use strict';!function(a,b){'function'==typeof define&&define['amd']?define('sablejs',b):'object'==typeof module&&module['exports']?module['exports']=b():a['sablejs']=b();}('undefined'!=typeof self?self:this,function(){return function(){var a0={},a1=a0['HAS_TYPED']='undefined'!=typeof Uint8Array&&'undefined'!=typeof Uint16Array,a2=!0x1,a3=!0x1;try{'a'===String['fromCharCode']['apply'](null,[0x61])&&(a2=!0x0);}catch(ew){}if(a1)try{'a'===String['fromCharCode']['apply'](null,new Uint8Array([0x61]))&&(a3=!0x0);}catch(ex){}a0['CAN_CHARCODE_APPLY']=a2,a0['CAN_CHARCODE_APPLY_TYPED']=a3,a0['APPLY_BUFFER_SIZE']=0xfffd,a0['APPLY_BUFFER_SIZE_OK']=null,a2=!0x1,(-0x1!=='abcほげ'['lastIndexOf']('ほげ',0x1)&&(a2=!0x0),a0['STRING_LASTINDEXOF_BUG']=a2),a0['BASE62TABLE']='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',(a3=a0['TABLE_LENGTH']='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk

Error: Uncaught ReferenceError: 'console' is not defined

Why did this happen?Can you give some examples for running?Thanks in advance

提示 compile failed: SyntaxError: Parenthesized pattern!

转换vite打包后的代码,报这个错,求解?谢谢
`
[ryan@ryanos dist]$ ~/soft/sablejs-linux-x64 -i sinan.es.js -o test.js

[ERROR] compile failed: SyntaxError: Parenthesized pattern (2:22)
at Parser. (/snapshot/sablejs-private-repo/node_modules/acorn/dist/acorn.js:3460:15)
at Parser.checkPatternErrors (/snapshot/sablejs-private-repo/node_modules/acorn/dist/acorn.js:773:29)
at Parser.toAssignable (/snapshot/sablejs-private-repo/node_modules/acorn/dist/acorn.js:1955:47)
at Parser.parseMaybeAssign (/snapshot/sablejs-private-repo/node_modules/acorn/dist/acorn.js:2476:23)
at Parser.parseVar (/snapshot/sablejs-private-repo/node_modules/acorn/dist/acorn.js:1299:26)
at Parser.parseVarStatement (/snapshot/sablejs-private-repo/node_modules/acorn/dist/acorn.js:1163:10)
at Parser.parseStatement (/snapshot/sablejs-private-repo/node_modules/acorn/dist/acorn.js:911:19)
at Parser.parseTopLevel (/snapshot/sablejs-private-repo/node_modules/acorn/dist/acorn.js:813:23)
at Parser.parse (/snapshot/sablejs-private-repo/node_modules/acorn/dist/acorn.js:586:17)
at Function.parse (/snapshot/sablejs-private-repo/node_modules/acorn/dist/acorn.js:636:37)
`

> @panchaowu

@panchaowu

var sm4=new SM4Util();
let encryptDataCBC = sm4.encryptData_CBC('this is a test'); // 'let' should be 'var'
print(encryptDataCBC); // 'console' and 'alert' not included in vm, so you should use 'print' instead

modify and run it, you will get

key error!
iv error!
prbpqTP2HDcvvm9snbKC9Q==

there is my original code and compiled result.

code.txt

output.txt

if you have any other problem, please reopen it, thanks.

多谢,

@panchaowu

var sm4=new SM4Util();
let encryptDataCBC = sm4.encryptData_CBC('this is a test'); // 'let' should be 'var'
print(encryptDataCBC); // 'console' and 'alert' not included in vm, so you should use 'print' instead

modify and run it, you will get

key error!
iv error!
prbpqTP2HDcvvm9snbKC9Q==

there is my original code and compiled result.

code.txt

output.txt

if you have any other problem, please reopen it, thanks.

多谢。顺便问一下,sablejs 2.0预计什么时间可以开源出来呢

Originally posted by @panchaowu in #25 (comment)

sablejs -v 报错: SyntaxError: Unexpected token .

[root@ecs-ab44 sablejs]# sablejs -v
(node:1978837) UnhandledPromiseRejectionWarning: /usr/local/lib/node_modules/sablejs/node_modules/string-kit/lib/format.js:327
markupTarget = this.shiftedMarkup?.[ runtime.shift ]?.[ markup ] ;
^

SyntaxError: Unexpected token .
at Module._compile (internal/modules/cjs/loader.js:723:23)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Module.require (internal/modules/cjs/loader.js:692:17)
at require (internal/modules/cjs/helpers.js:25:18)
at Object. (/usr/local/lib/node_modules/sablejs/node_modules/string-kit/lib/string.js:62:2)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
(node:1978837) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:1978837) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

能否支持commonjs/umd的模块导出

一般自己打包后的代码,或者很多第三方库都是umd导出的,能否提供支持在run之后直接返回导出的内容,这样在易用性上和对现有项目的兼容上会好很多。

举一个场景,在一个项目里,只对不涉及dom的语言或者加密算法模块进行保护,其他模块不需要改动。

有关浏览器环境配置

需求:希望在浏览器端运行代码混淆demo。

代码:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/runtime.js"></script>
<script>
var vm = new (sablejs ())()
vm.run('JTI1N0IlMjUyMm5hbWUlMjUyMiUyNTNBJTI1MjIlMjUyMiUyNTJDJTI1MjJzY3JpcHQlMjUyMiUyNTNBdHJ1ZSUyNTJDJTI1MjJzdHJpY3QlMjUyMiUyNTNBZmFsc2UlMjUyQyUyNTIybGlnaHR3ZWlnaHQlMjUyMiUyNTNBZmFsc2UlMjUyQyUyNTIyYXJndW1lbnRzJTI1MjIlMjUzQWZhbHNlJTI1MkMlMjUyMm51bXBhcmFtcyUyNTIyJTI1M0EwJTI1MkMlMjUyMnBzJTI1MjIlMjUzQSUyNTVCJTI1NUQlMjUyQyUyNTIydnQlMjUyMiUyNTNBJTI1NUIlMjUyMmZpYiUyNTIyJTI1NUQlMjUyQyUyNTIyZnQlMjUyMiUyNTNBJTI1NUIlMjU3QiUyNTIybmFtZSUyNTIyJTI1M0ElMjUyMmZpYiUyNTIyJTI1MkMlMjUyMnNjcmlwdCUyNTIyJTI1M0FmYWxzZSUyNTJDJTI1MjJzdHJpY3QlMjUyMiUyNTNBZmFsc2UlMjUyQyUyNTIybGlnaHR3ZWlnaHQlMjUyMiUyNTNBdHJ1ZSUyNTJDJTI1MjJhcmd1bWVudHMlMjUyMiUyNTNBZmFsc2UlMjUyQyUyNTIybnVtcGFyYW1zJTI1MjIlMjUzQTElMjUyQyUyNTIycHMlMjUyMiUyNTNBJTI1NUIlMjUyMm4lMjUyMiUyNTVEJTI1MkMlMjUyMnZ0JTI1MjIlMjUzQSUyNTVCJTI1MjJuJTI1MjIlMjUyQyUyNTIyZmliJTI1MjIlMjU1RCUyNTJDJTI1MjJmdCUyNTIyJTI1M0ElMjU1QiUyNTVEJTI1MkMlMjUyMnN0JTI1MjIlMjUzQSUyNTVCJTI1NUQlMjUyQyUyNTIybnQlMjUyMiUyNTNBJTI1NUIyJTI1MkMxJTI1NUQlMjUyQyUyNTIyZXQlMjUyMiUyNTNBJTI1NUIlMjU1RCUyNTJDJTI1MjJkZnQlMjUyMiUyNTNBJTI1NUIlMjU1RCUyNTJDJTI1MjJvcGNvZGUlMjUyMiUyNTNBJTI1NUIxOSUyNTJDMjIlMjUyQzIlMjUyQzAlMjUyQzIwJTI1MkMxJTI1MkM3JTI1MkMwJTI1MkMxMDUlMjUyQzg0JTI1MkMzNCUyNTJDMjAlMjUyQzIlMjUyQzE0JTI1MkMyMCUyNTJDMSUyNTJDNyUyNTJDMSUyNTJDOTclMjUyQzQzJTI1MkMxJTI1MkMyMCUyNTJDMiUyNTJDMTQlMjUyQzIwJTI1MkMxJTI1MkM3JTI1MkMwJTI1MkM5NyUyNTJDNDMlMjUyQzElMjUyQzU3JTI1MkM4MyUyNTJDMzYlMjUyQzIwJTI1MkMxJTI1MkM4NiUyNTJDMTQlMjUyQzg2JTI1NUQlMjU3RCUyNTVEJTI1MkMlMjUyMnN0JTI1MjIlMjUzQSUyNTVCJTI1NUQlMjUyQyUyNTIybnQlMjUyMiUyNTNBJTI1NUIxMCUyNTVEJTI1MkMlMjUyMmV0JTI1MjIlMjUzQSUyNTVCJTI1NUQlMjUyQyUyNTIyZGZ0JTI1MjIlMjUzQSUyNTVCJTI1NUQlMjUyQyUyNTIyb3Bjb2RlJTI1MjIlMjUzQSUyNTVCOSUyNTJDMCUyNTJDMjIlMjUyQzElMjUyQzAlMjUyQzE0JTI1MkMwJTI1MkMyMCUyNTJDMSUyNTJDMTQlMjUyQzclMjUyQzAlMjUyQzQzJTI1MkMxJTI1MkM4NiUyNTVEJTI1N0Q=')
</script>

以上代码不会报错,似乎虚拟机正常运行,但原始代码中删去了范例代码里的console.log部分,如果加入则会提示console不存在。请问想调用浏览器内建组件,比如console,document,window,screen这些应该怎么做,谢谢。

运行报错 (win10) Download.js:265

通过淘宝镜像安装后 (npm i sablejs -g --registry=https://registry.npm.taobao.org)

执行 sablejs -i fib.js -o output ,返回: 'sablejs' 不是内部或外部命令,也不是可运行的程序
改为执行 node cli.js -i example\fib.js -o output 返回如下:

D:\Prg\npm\sablejs\node_modules\nodejs-file-downloader\Download.js:265
for await (let chunk of stream) {
^^^^^

SyntaxError: Unexpected reserved word
at createScript (vm.js:80:10)
at Object.runInThisContext (vm.js:139:10)
at Module._compile (module.js:616:28)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Module.require (module.js:596:17)
at require (internal/module.js:11:18)
at Object. (D:\OwenWorks\npm\sablejs\node_modules\nodejs-file-downloader\Downloader.js:3:18)

是我哪个步骤不对吗

macbook pro m1 error

lifuhaideMacBook-Pro:utils lifuhai$ sablejs -i test.js -o output
[INFO] start download compiler: sablejs-osx-arm64...
[ERROR] download failed: Request timed out

不支持 ARM 环境吗?

[INFO] start download compiler: sablejs-osx-arm64...
[ERROR] download failed: Request failed with status code 404 

关于浏览器环境下的配置

您好,看您在介绍中,浏览器环境下的代码中有:const VM = require("sablejs/runtime")();
我要做的就是加密网站中的JS代码
但我在实际测试中,浏览器控制台提示无法使用require,然后创建require变量后,说require不是函数,改成了require("runtime.js文件路径"),然后console.log(VM)的时候反馈undefined。
所以,想咨询一下,不是在node.js下,而是作为加密网站JS代码功能使用的时候,我做错了什么呢?菜鸟一枚,多谢指教!

@zdrin If you want to new EventDispatcher, you should bind cax to sablejs, like cax4sablejs doc said:

@zdrin If you want to new EventDispatcher, you should bind cax to sablejs, like cax4sablejs doc said:

const cax4sablejs = require("cax4sablejs");
const VM = require("sablejs/runtime")();
const vm = new VM();
cax4sablejs(vm); // that's all!

If you want to call sablejs inner function from outside, you can use vm.call, for example:

// a function is on sablejs inner enviorment
function a(){
  print('a');
}
// get sablejs inner function and call it
const global = vm.getGlobal();
const vA = vm.getProperty(global, 'a');
vm.call(vA, vm.createUndefined()); // will log 'a'

Originally posted by @ErosZy in #10 (comment)

模仿ajax4sablejs写了一个demo,运行报错

output.js 代码如下:

ajax({
  url: "https://api.apishop.net/common/weather/get15DaysWeatherByArea",
  method: "GET",
  success: function (response) {
    console.log(response);
  },
});

执行 sablejs -i output.js -o output.txt

外层的JS代码

function App() {
  useEffect(() => {
    console.log("starting");
    
    const VM = require("sablejs/runtime")();
    const vm = new VM();

    const vGlobal = vm.getGlobal();
    const vConsole = vm.createObject();
    const vLog = vm.createFunction("log", function () {
      const temp = [];
      for (let i = 0; i < arguments.length; i++) {
        temp.push(vm.asString(arguments[i]));
      }

      console.log(...temp);
      return vm.createUndefined();
    });

    const vAjax = vm.createFunction("ajax", function (vOption) {
      const option = {};
      const vUrl = vm.getProperty(vOption, "url");
      const vMethod = vm.getProperty(vOption, "method");
      const vSuccess = vm.getProperty(vOption, "success");

      if (vm.isString(vUrl)) {
        option.url = vm.asString(vUrl);
      }
  
      if (vm.isString(vMethod)) {
        option.method = vm.asString(vMethod);
      }
  
      if (vm.isFunction(vSuccess)) {
        option.success = function(response) {
          vm.call(
            vSuccess,
            vm.createUndefined(),
            vm.createString(response)
          );
        };
      }

      function send(option) {
        let xhr = new XMLHttpRequest();
        xhr.withCredentials = option.withCredentials || false;
        xhr.open((option.method || "GET").toLowerCase(), option.url, true);
      
        xhr.onload = () => {
          if (xhr.readyState === 4 && xhr.status === 200) {
            const response = xhr.responseText;
            console.log(response);
            option.success(response);
          }
        };

        xhr.send(null);
      }
  
      send(option);
      return vm.createUndefined();
    });

    vm.setProperty(vGlobal, "ajax", vAjax);
    vm.setProperty(vConsole, "log", vLog);
    vm.setProperty(vGlobal, "console", vConsole);

    (async () => {
      const resp = await fetch("./output.txt");
      const data = await resp.text();
      vm.run(data);
      vm.destroy();
    })();
  }, []);

  return <div>JS safty</div>;
}

运行时报这个错误

runtime.js:1 Uncaught TypeError: Cannot read properties of null (reading 'top')
    at ef.call (runtime.js:1:1)
    at Object.option.success (index.js:40:1)
    at XMLHttpRequest.xhr.onload (index.js:57:1)

如何创建一个指定了具体值的包装Array?

我试着这样创建一个 [1,2,3] 的包装对象,但貌似有问题,请大佬指教一下

const vArray = vm.createArray(3);
vm.setProperty(vArray, 0, vm.createNumber(1))
vm.setProperty(vArray, 1, vm.createNumber(2))
vm.setProperty(vArray, 2, vm.createNumber(3))

这能正确工作,是我搞错了,抱歉

请问浏览器端怎么运行呢?

我使用sable.js编译了代码,然后引入了运行时<script src="https://cdn.jsdelivr.net/npm/[email protected]/runtime.js"></script>
然后我就这样使用,结果无法使用

// 编译后的代码
var code = 'JTI1N0IlMjUyMm5hbWUlMjUyMI1MjJzdHJpY3QlMjUyMiUyNTN';

var vm = new sablejs();
// 发现vm还是一个函数
console.log(vm)
// source should be base64 string via sablejs compiling
vm.run('<compile source ' + code + '>');

Compiler source code / Redistribute MacOS compiler binary

The distributed binary for MacOS sablejs-osx-x64, doesn't seem to work for me on macOS 12.5.1.
It runs and terminates with exit code 4, producing no output file.
I verified that it is not something wrong with the JavaScript input file - compiling it on Linux using the supplied binary on Ubuntu 20.04 works properly.

Can't seem to find the source code to self-compile my own binary.
Is it made public somewhere?
Otherwise, recompile & redistribute the MacOS binary, after verifying if it is indeed broken somewhere.

Attached sablejs-osx-x64.zip

在Node环境下运行cli提示`import` 已经被占用

尝试使用Windows 10来编译(WSL炸了所以没法用),环境是React17+NodeJS16,,提示import关键字被占用了,导致无法编译想要的文件

项目通过create-react-app创建,尝试在目录中运行sablejs src/components/request.js

[ERROR] compile failed: SyntaxError: The keyword 'import' is reserved (1:0)
    at Parser.<anonymous> (C:\snapshot\sablejs-private-repo\node_modules\acorn\dist\acorn.js:3460:15)
    at Parser.checkUnreserved (C:\snapshot\sablejs-private-repo\node_modules\acorn\dist\acorn.js:3367:12)
    at Parser.parseIdent (C:\snapshot\sablejs-private-repo\node_modules\acorn\dist\acorn.js:3396:12)
    at Parser.parseExprAtom (C:\snapshot\sablejs-private-repo\node_modules\acorn\dist\acorn.js:2759:21)
    at Parser.parseExprSubscripts (C:\snapshot\sablejs-private-repo\node_modules\acorn\dist\acorn.js:2627:21)
    at Parser.parseMaybeUnary (C:\snapshot\sablejs-private-repo\node_modules\acorn\dist\acorn.js:2593:19)
    at Parser.parseExprOps (C:\snapshot\sablejs-private-repo\node_modules\acorn\dist\acorn.js:2520:21)
    at Parser.parseMaybeConditional (C:\snapshot\sablejs-private-repo\node_modules\acorn\dist\acorn.js:2503:21)
    at Parser.parseMaybeAssign (C:\snapshot\sablejs-private-repo\node_modules\acorn\dist\acorn.js:2470:21)
    at Parser.parseExpression (C:\snapshot\sablejs-private-repo\node_modules\acorn\dist\acorn.js:2433:21)

vm.call can‘t return value

I did the following tests according to the instructions, but I couldn't get the expected results.

// inside vm compiled to file 'output'

function fib(n) {
  return n < 2 ? n : fib(n - 1) + fib(n - 2);
}

var start = Date.now();
console.log("[INFO] fib: " + fib(30));
console.log("[INFO] time consuming: " + (Date.now() - start) + "ms");

function sign(data) {
   return data + ", do what you want";
}
<html>
<body>
<script src="./runtime.js"></script>
<script>
       var vm = new (sablejs())();

	const vGlobal = vm.getGlobal();
	const vConsole = vm.createObject();
	const vLog = vm.createFunction("log", function () {
	  const temp = [];
	  for (let i = 0; i < arguments.length; i++) {
	    temp.push(vm.asString(arguments[i]));
	  }

	  console.log(...temp);
	  return vm.createUndefined();
	});

	vm.setProperty(vConsole, "log", vLog);
	vm.setProperty(vGlobal, "console", vConsole);

	(async () => {
	  const resp = await fetch("output");
	  const data = await resp.text();
	  vm.run(data);

	  const vFunc = vm.getProperty(vGlobal, 'sign');
	 var vFunc_Ret=vm.call(vFunc, vm.createString("Hello World!"));
	 const vFuncValue = vm.asString(vFunc_Ret);
	 console.log("return:",vFuncValue); // <-----

	  vm.destroy();
	})();
</script>
</body>
</html>

Finally, the return is:
return: function sign(data){ [byte code] }

Why not "Hello World! , do what you want"

关于核心函数入口的保护 来自issues#12的疑问

#12

在issues#12展示了,如何在外部调用使用sablejs保护的函数。但是存在的问题是,这个函数的入口和出口在前端是可以调试出来的,只是函数内部的逻辑不可见。也就是前端可以解耦出关键函数来,不需要知道内部具体逻辑,方便爬虫或者攻击。

由于sablejs的vm里不支持location对象,所以通过限制域名在当前域名下使用也行不通。
那么sablejs如何才能有效避免函数劫持呢?今后有这方便计划么?

Open-source without source?

runtime.js is unfortunately very compressed.
Is this project open-sourced in any way? Or this is just a repo for the final npm artifacts?
Thank you for the work. But as this is pretty much MIT-ish licensed, without source code, hardly can we trust any project with it.

the plan for sablejs 2.0

sablejs 1.x has been running well on YoTest, but because of the private opcode design, we don't open all code for community(preventing decompiling). After discussions, sablejs 2.0 will plan to open all code! To reach this goal, we will make a major upgrade to the current code of 1.x, include:

1. remove the private opcode design and transform for the stack operation directly
2. dynamically import inner object for sandbox

1. Transform Directly

Since sablejs 1.x is fully stack-based vm, we will have a big loop to execute the relevant opcode code. However, this approach causes a significant performance loss due to the failure of branch prediction:

for (;;) {
  switch (opcode) {
    case 0: {
      // ...
    }
    case 1: {
      // ...
    }
    case 2: {
      // ...
    }
  }
}

After using the latest d8 and perf for profiling, about 30% of the performance consumption is wasted. To solve this problem, and to make V8 better able to help us with JIT, we will perform the equivalent transform operation directly, consider the following example:

function add() {
  return 1 + 2 + 3;
}

In 1.x, we will get linear opcode bytes, which will then be executed by vm. But in 2.0, we will compile directly to the following code:

function __C_add(J) {
  __pushNumber(J, 1);
  __pushNumber(J, 2);
  __R_add(J);
  __pushNumber(J, 3);
  __R_add(J);
}

Here, stack manipulation methods such as __pushNumber are still provided by runtime. In this way, together with the relevant compilation optimizations in 1.x, there will be a relatively large performance improvment for frequent execution(benchmark can be followed by DoppioJVM Web JIT implement).

(function(){
  for(var i = 0; i < 10000000; i++);
}());

// sablejs 2.0: 276.279ms --- baseline
// sablejs 1.0.6 878ms --- slower: 218.11%
// quickjs-wasm: 228ms --- faster: 17.39%

At the same time, this brings the benefit of not having to rely on opcode, so we can directly open all of sablejs's code.

2. Dynamically Import Inner Object

In most of the usage of the current feedback, it is basically using sablejs for JSVMP. But sablejs 1.x is mainly designed with sandbox as the core, after 2.0 we will mainly aim at JSVMP while taking into account the functionality of sandbox,(the Inner Object will be import on demand according to your needs).

Also, when you are using JSVMP only, sablejs 2.0 will get a very big performance improvment thanks to V8's object optimization!

I considered for releasing sablejs 2.0 in mid-2022, please look forward to it! 😁

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.