brianhenryie / bh-wc-checkout-rate-limiter Goto Github PK
View Code? Open in Web Editor NEWPrevent card-attack fraud on WooCommerce stores by rate-limiting the "place order" button
License: GNU General Public License v2.0
Prevent card-attack fraud on WooCommerce stores by rate-limiting the "place order" button
License: GNU General Public License v2.0
Describe the current, buggy behavior
On the logs page, when the "Log date" dropdown is changed, the text following "Displaying log file at" changes, but the actual file link does not. The link continues to point to the original file that was displayed for the current date.
Describe how other contributors can replicate this bug
// You can also use code snippets if needed.
Describe what you would expect as the correct outcome
Update the file link to download the log for the selected date.
Let us know what environment you are running this on
OS: Linux 5.15.0-89-generic #99-Ubuntu SMP Mon Oct 30 20:42:41 UTC 2023 x86_64
Shell: /bin/bash
PHP binary: /usr/bin/php7.4
PHP version: 7.4.33
php.ini used: /etc/php/7.4/cli/php.ini
MySQL binary: /usr/bin/mysql
MySQL version: mysql Ver 15.1 Distrib 10.6.12-MariaDB, for debian-linux-gnu (x86_64) using EditLine wrapper
SQL modes:
WP-CLI root dir: phar://wp-cli.phar/vendor/wp-cli/wp-cli
WP-CLI vendor dir: phar://wp-cli.phar/vendor
WP_CLI phar path: /var/www/html
WP-CLI packages dir:
WP-CLI global config:
WP-CLI project config:
WP-CLI version: 2.5.0
Provide additional context/Screenshots
Add any other context about the problem here.
If applicable, add screenshots to help explain (you can just drag&drop images into the Github issue).
I downloaded a zip file of the plugin and then uploaded it to WordPress. It loaded without issue but failed to activate with the following errors:
Warning: require_once(/home/xxxxxxxxx/public_html/wp-content/plugins/bh-wc-checkout-rate-limiter-master/vendor-prefixed/autoload.php): failed to open stream: No such file or directory in /home/xxxxxxxxx/public_html/wp-content/plugins/bh-wc-checkout-rate-limiter-master/autoload.php on line 18
Fatal error: require_once(): Failed opening required '/home/xxxxxxxxx/public_html/wp-content/plugins/bh-wc-checkout-rate-limiter-master/vendor-prefixed/autoload.php' (include_path='.:/opt/alt/php74/usr/share/pear') in /home/xxxxxxxxx/public_html/wp-content/plugins/bh-wc-checkout-rate-limiter-master/autoload.php on line 18
Can you suggest a fix?
Thanks
I'm looking for a few pointers on how to best use this plugin, with the goal of stopping card checking activity while minimizing the annoyance of real customers. For the rate limit settings I have 2 attempts per 60 seconds, 3 attempts per 180 seconds, and nothing in the third row. In the logs I mostly see carders hitting the first limit so maybe the subsequent ones don't matter as much. I'll take any advice on offer. Thanks!
Hello, Brian! Last year my client's WooCommerce site was hit by a credit card testing attack. So, I added reCaptcha to the checkout page and tightened some things in WordFence. Their site was hit again with another cc testing attack late last week. The Captcha plugin, as best as I can tell, works, so I've no idea how they managed to execute the script after the Captcha test is satisfied, but 1674 orders were placed by the same IP within 60 minutes. That's 27.9 attempts per minute, or one attempt every 2.15 seconds. My guess is they're using some type of Python / Selenium based script.
I'm thinking of adding your plugin to my client's website, because once an attacker's script clicks the 'Place Order' button beyond twice inside of 60 seconds, the order will stop processing, correct? That tells me once the hacker revisits his work, he'll move on to easier prey once he realizes his unsatisfactory results.
Is that the gist of how your plugin works? Are you hearing success stories from the people who have utilized it?
Thanks for your time,
Keith Robideau
Installed this on a client's site after using it on other client sites with no issue. It created a fatal error that may have to do with a conflict with Jetpack:
[11-Oct-2022 02:40:33 UTC] PHP Fatal error: Uncaught TypeError: Argument 3 passed to BrianHenryIE\Checkout_Rate_Limiter\Admin\Plugins_Page::action_links() must be of the type array, null given, called in /chroot/home/server_name_removed/html/wp-includes/class-wp-hook.php on line 307 and defined in /chroot/home/server_name_removed/html/wp-content/plugins/bh-wc-checkout-rate-limiter/Admin/class-plugins-page.php:57 Stack trace: #0 /chroot/home/server_name_removed/html/wp-includes/class-wp-hook.php(307): BrianHenryIE\Checkout_Rate_Limiter\Admin\Plugins_Page->action_links(Array, 'bh-wc-checkout-...', NULL, 'all') #1 /chroot/home/server_name_removed/html/wp-includes/plugin.php(191): WP_Hook->apply_filters(Array, Array) #2 /chroot/home/server_name_removed/html/wp-content/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-callables.php(380): apply_filters('plugin_action_l...', Array, 'bh-wc-checkout-...', NULL, 'all') #3 /chroot/home/server_name_removed/html/wp-includes/class-wp- in /chroot/home/server_name_removed/html/wp-content/plugins/bh-wc-checkout-rate-limiter/Admin/class-plugins-page.php on line 57
Getting the following error when activating the latest version of the plugin:
Parse error: syntax error, unexpected 'Logger_Settings_Interface' (T_STRING), expecting function (T_FUNCTION) or const (T_CONST) in /Users/korymathis/Local Sites/healer/app/public/wp-content/plugins/bh-wc-checkout-rate-limiter/strauss/brianhenryie/wp-logger/src/Includes/class-bh-wp-logger.php on line 38
When attempting to use WP-CLI , the command line crashes because it's unable to write the file.
It would probably need a try/catch or another logging location.
# sudo -u wpcli wp redis flush
PHP Fatal error: Uncaught RuntimeException: The file could not be written to. Check that appropriate permissions have been set. in /var/www/html/wordpress/web/app/plugins/bh-wc-checkout-rate-limiter/strauss/katzgrau/klogger/src/Logger.php:134
Stack trace:
#0 /var/www/html/wordpress/web/app/plugins/bh-wc-checkout-rate-limiter/strauss/brianhenryie/wp-logger/src/includes/class-bh-wp-logger.php(140): BrianHenryIE\Checkout_Rate_Limiter\Katzgrau\KLogger\Logger->__construct()
#1 /var/www/html/wordpress/web/app/plugins/bh-wc-checkout-rate-limiter/strauss/brianhenryie/wp-logger/src/class-logger.php(23): BrianHenryIE\Checkout_Rate_Limiter\BrianHenryIE\WP_Logger\includes\BH_WP_Logger->__construct()
#2 /var/www/html/wordpress/web/app/plugins/bh-wc-checkout-rate-limiter/strauss/brianhenryie/wp-logger/src/class-logger.php(40): BrianHenryIE\Checkout_Rate_Limiter\BrianHenryIE\WP_Logger\Logger->__construct()
#3 /var/www/html/wordpress/web/app/plugins/bh-wc-checkout-rate-limiter/bh-wc-checkout-rate-limiter.php(64): BrianHenryIE\Checkout_Rate_L in /var/www/html/wordpress/web/app/plugins/bh-wc-checkout-rate-limiter/strauss/katzgrau/klogger/src/Logger.php on line 134
Hey there,
Testing out the plugin and its nice and simple. May I suggest to make the troubleshooting process of failed orders easier that you write into the order notes of the failed order the fact that the rate limit plugin was the one that blocked the order. This way blocked orders that are really false positives can be identified easier by someone providing support.
We keep getting this error on the admin pages:
Checkout Rate Limiter. Error: "Uncaught Error: Call to undefined function BrianHenryIE\Checkout_Rate_Limiter\WooCommerce\wc_get_current_admin_url() in /chroot/home/server_name_removed/html/wp-content/plugins/bh-wc-checkout-rate-limiter/WooCommerce/class-settings-payments.php:59
Stack trace:
#0 /chroot/home/server_name_removed/html/wp-includes/class-wp-hook.php(307): BrianHenryIE\Checkout_Rate_Limiter\WooCommerce\Settings_Payments->record_page_visit_time(Object(WP_Screen))
#1 /chroot/home/server_name_removed/html/wp-includes/class-wp-hook.php(331): WP_Hook->apply_filters(NULL, Array)
#2 /chroot/home/server_name_removed/html/wp-includes/plugin.php(476): WP_Hook->do_action(Array)
#3 /chroot/home/server_name_removed/html/wp-admin/includes/class-wp-screen.php(422): do_action('current_screen', Object(WP_Screen))
#4 /chroot/home/server_name_removed/html/wp-admin/includes/screen.php(243): WP_Screen->set_current_screen()
#5 /chroot/home/server_name_removed/html/wp-content/plugins" at 2022-10-11T03:02:39Z UTC.
Hello!
This looks like a great plugin, thanks for putting in the time to make it.
I've installed on my development environment, however once I click checkout on the checkout page the following fatal error is thrown:
PHP Fatal error: Declaration of BrianHenryIE\Checkout_Rate_Limiter\WpOop\TransientCache\CachePool::get($key, $default = null) must be compatible with Psr\SimpleCache\CacheInterface::get(string $key, mixed $default = null): mixed in /srv/www/devsite/current/web/app/plugins/bh-wc-checkout-rate-limiter/vendor-prefixed/wp-oop/transient-cache/src/CachePool.php on line 93
Here's the logs
2022-11-23T10:05:23+00:00 DEBUG Checking 192.168.57.1 rate limit 2 per 60 seconds.
{"debug_backtrace":[{"file":"\/srv\/www\/devsite\/current\/web\/app\/plugins\/bh-wc-checkout-rate-limiter\/src\/WooCommerce\/class-ajax.php","lineNumber":78,"arguments":[],"applicationFrame":false,"method":"[top]","class":null}],"filters":["template_redirect","wc_ajax_checkout"]}
2022-11-23T10:05:23+00:00 ERROR Declaration of BrianHenryIE\Checkout_Rate_Limiter\WpOop\TransientCache\CachePool::get($key, $default = null) must be compatible with Psr\SimpleCache\CacheInterface::get(string $key, mixed $default = null): mixed
{"type":64,"message":"Declaration of BrianHenryIE\\Checkout_Rate_Limiter\\WpOop\\TransientCache\\CachePool::get($key, $default = null) must be compatible with Psr\\SimpleCache\\CacheInterface::get(string $key, mixed $default = null): mixed","file":"\/srv\/www\/devsite\/current\/web\/app\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/wp-oop\/transient-cache\/src\/CachePool.php","line":93,"debug_backtrace":[{"file":"\/srv\/www\/devsite\/current\/web\/app\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/brianhenryie\/bh-wp-logger\/src\/PHP\/class-php-shutdown-handler.php","lineNumber":87,"arguments":[],"applicationFrame":true,"method":"handle","class":"BrianHenryIE\\Checkout_Rate_Limiter\\WP_Logger\\PHP\\PHP_Shutdown_Handler"},{"file":"unknown","lineNumber":0,"arguments":[],"applicationFrame":false,"method":"[top]","class":null}],"filters":["template_redirect","wc_ajax_checkout"]}
Looks like it's due to the difference in some implementations, here's Psr\SimpleCache\CacheInterface:
public function get(string $key, mixed $default = null): mixed;
This is BrianHenryIE\Checkout_Rate_Limiter\WpOop\TransientCache\CachePool
public function get($key, $default = null)
In theory it should be a simple change of definitions.
Env details:
WC Version: 5.8.1
WP Version: 5.9.3
PHP Version: 8.0.17
Firstly thank you for this plugin - hugely useful for me to help prevent card testing and it's much appreciated. I'm getting a fatal error on installation...
Warning: require_once(/www/wp-content/plugins/bh-wc-checkout-rate-limiter-1.2.0/vendor-prefixed/autoload.php): failed to open stream: No such file or directory in /www/wp-content/plugins/bh-wc-checkout-rate-limiter-1.2.0/autoload.php on line 39 Fatal error: require_once(): Failed opening required '/www/wp-content/plugins/bh-wc-checkout-rate-limiter-1.2.0/vendor-prefixed/autoload.php' (include_path='.:/usr/share/php') in /www/wp-content/plugins/bh-wc-checkout-rate-limiter-1.2.0/autoload.php on line 39
Not a huge amount of other plugins installed.
PHP: 7.4
Wordpress: 6.02
WooCommerce: 7.0.0
Fatal Error:
Stack trace:
#0 /home/customer/www/domain.com/public_html/wp-content/plugins/bh-wc-checkout-rate-limiter/vendor-prefixed/wp-oop/transient-cache/src/CachePool.php(137): BrianHenryIE\Checkout_Rate_Limiter\WpOop\TransientCache\CachePool->setTransient('checkout/checko...', Array, 60)
#1 /home/customer/www/domain.com/public_html/wp-content/plugins/bh-wc-checkout-rate-limiter/vendor-prefixed/nikolaposa/rate-limit/src/Psr16RateLimiter.php(129): BrianHenryIE\Checkout_Rate_Limiter\WpOop\TransientCache\CachePool->set('checkout/checko...', Array, 60)
#2 /home/customer/www/domain.com/public_html/wp-content/plugins/bh-wc-checkout-rate-limiter/vendor-prefixed/nikolaposa/rate-limit/src/Psr16RateLimiter.php(59): BrianHenryIE\Checkout_Rate
{"type":1,"message":"Uncaught RuntimeException: set_transient() failed with key \"checkout\/checkout74.80.182.73-60\" with TTL 60s in \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/wp-oop\/transient-cache\/src\/CachePool.php:322\nStack trace:\n#0 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/wp-oop\/transient-cache\/src\/CachePool.php(137): BrianHenryIE\\Checkout_Rate_Limiter\\WpOop\\TransientCache\\CachePool->setTransient('checkout\/checko...', Array, 60)\n#1 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/nikolaposa\/rate-limit\/src\/Psr16RateLimiter.php(129): BrianHenryIE\\Checkout_Rate_Limiter\\WpOop\\TransientCache\\CachePool->set('checkout\/checko...', Array, 60)\n#2 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/nikolaposa\/rate-limit\/src\/Psr16RateLimiter.php(59): BrianHenryIE\\Checkout_Rate","file":"\/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/wp-oop\/transient-cache\/src\/CachePool.php","line":140,"debug_backtrace":[{"file":"\/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/brianhenryie\/bh-wp-logger\/src\/api\/class-bh-wp-psr-logger.php","lineNumber":80,"arguments":["error","Uncaught RuntimeException: set_transient() failed with key \"checkout\/checkout74.80.182.73-60\" with TTL 60s in \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/wp-oop\/transient-cache\/src\/CachePool.php:322\nStack trace:\n#0 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/wp-oop\/transient-cache\/src\/CachePool.php(137): BrianHenryIE\\Checkout_Rate_Limiter\\WpOop\\TransientCache\\CachePool->setTransient('checkout\/checko...', Array, 60)\n#1 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/nikolaposa\/rate-limit\/src\/Psr16RateLimiter.php(129): BrianHenryIE\\Checkout_Rate_Limiter\\WpOop\\TransientCache\\CachePool->set('checkout\/checko...', Array, 60)\n#2 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/nikolaposa\/rate-limit\/src\/Psr16RateLimiter.php(59): BrianHenryIE\\Checkout_Rate",{"type":1,"message":"Uncaught RuntimeException: set_transient() failed with key \"checkout\/checkout74.80.182.73-60\" with TTL 60s in \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/wp-oop\/transient-cache\/src\/CachePool.php:322\nStack trace:\n#0 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/wp-oop\/transient-cache\/src\/CachePool.php(137): BrianHenryIE\\Checkout_Rate_Limiter\\WpOop\\TransientCache\\CachePool->setTransient('checkout\/checko...', Array, 60)\n#1 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/nikolaposa\/rate-limit\/src\/Psr16RateLimiter.php(129): BrianHenryIE\\Checkout_Rate_Limiter\\WpOop\\TransientCache\\CachePool->set('checkout\/checko...', Array, 60)\n#2 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/nikolaposa\/rate-limit\/src\/Psr16RateLimiter.php(59): BrianHenryIE\\Checkout_Rate","file":"\/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/wp-oop\/transient-cache\/src\/CachePool.php","line":140}],"applicationFrame":true,"method":"log","class":"BrianHenryIE\\Checkout_Rate_Limiter\\WP_Logger\\API\\BH_WP_PSR_Logger"},{"file":"\/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/brianhenryie\/bh-wp-logger\/src\/api\/class-bh-wp-psr-logger.php","lineNumber":52,"arguments":["Uncaught RuntimeException: set_transient() failed with key \"checkout\/checkout74.80.182.73-60\" with TTL 60s in \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/wp-oop\/transient-cache\/src\/CachePool.php:322\nStack trace:\n#0 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/wp-oop\/transient-cache\/src\/CachePool.php(137): BrianHenryIE\\Checkout_Rate_Limiter\\WpOop\\TransientCache\\CachePool->setTransient('checkout\/checko...', Array, 60)\n#1 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/nikolaposa\/rate-limit\/src\/Psr16RateLimiter.php(129): BrianHenryIE\\Checkout_Rate_Limiter\\WpOop\\TransientCache\\CachePool->set('checkout\/checko...', Array, 60)\n#2 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/nikolaposa\/rate-limit\/src\/Psr16RateLimiter.php(59): BrianHenryIE\\Checkout_Rate",{"type":1,"message":"Uncaught RuntimeException: set_transient() failed with key \"checkout\/checkout74.80.182.73-60\" with TTL 60s in \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/wp-oop\/transient-cache\/src\/CachePool.php:322\nStack trace:\n#0 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/wp-oop\/transient-cache\/src\/CachePool.php(137): BrianHenryIE\\Checkout_Rate_Limiter\\WpOop\\TransientCache\\CachePool->setTransient('checkout\/checko...', Array, 60)\n#1 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/nikolaposa\/rate-limit\/src\/Psr16RateLimiter.php(129): BrianHenryIE\\Checkout_Rate_Limiter\\WpOop\\TransientCache\\CachePool->set('checkout\/checko...', Array, 60)\n#2 \/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/nikolaposa\/rate-limit\/src\/Psr16RateLimiter.php(59): BrianHenryIE\\Checkout_Rate","file":"\/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/wp-oop\/transient-cache\/src\/CachePool.php","line":140}],"applicationFrame":true,"method":"error","class":"BrianHenryIE\\Checkout_Rate_Limiter\\WP_Logger\\API\\BH_WP_PSR_Logger"},{"file":"\/home\/customer\/www\/domain.com\/public_html\/wp-content\/plugins\/bh-wc-checkout-rate-limiter\/vendor-prefixed\/brianhenryie\/bh-wp-logger\/src\/php\/class-php-shutdown-handler.php","lineNumber":87,"arguments":[],"applicationFrame":true,"method":"handle","class":"BrianHenryIE\\Checkout_Rate_Limiter\\WP_Logger\\PHP\\PHP_Shutdown_Handler"},{"file":"unknown","lineNumber":0,"arguments":[],"applicationFrame":false,"method":"[top]","class":null}],"filters":["template_redirect","wc_ajax_checkout"]}
Parse error: syntax error, unexpected 'LoggerInterface' (T_STRING), expecting function (T_FUNCTION) or const (T_CONST) in /public_html/wp-content/plugins/bh-wc-checkout-rate-limiter/Mozart/PSR-04/BrianHenryIE/WP_Logger/admin/class-admin.php on line 17
Thank you for creating this plugin! ๐
We are using the CheckoutWC plugin and it does not use the wc_ajax_checkout
hook, so your rate limiting is not triggered/checked.
We modified your plugin, to use it on our site, but would prefer some additional hooks that we can use and extend its functionality, so we can update your plugin regularly.
Action hook 1:
Here https://github.com/BrianHenryIE/bh-wc-checkout-rate-limiter/blob/master/src/class-bh-wc-checkout-rate-limiter.php#L97, we would need an additional action hook, so we can hook the CheckoutWC ajax hook to your rate limiter. We added this line:
add_action( 'cfw_before_process_checkout', array( $ajax, 'rate_limit_checkout' ), 0 );
, so we would need an action hook, like do_action( 'bh_wcr_define_woocommerce_ajax_hooks', $ajax );
in that method.
Action hook2:
Here https://github.com/BrianHenryIE/bh-wc-checkout-rate-limiter/blob/master/src/woocommerce/class-ajax.php#L113, right before the json error response, we would need another action item, because CheckoutWC is expecting a different response, so adding an action hook like this would be awesome: do_action( 'bh_wcr_checkout_rate_limit_exceeded', $rate_limiter );
I'll prepare a quick PR so it will be easier for you, but I don't know which prefix to use for these hooks, because I can see mixed ones being used in the plugin.
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.