Git Product home page Git Product logo

oasisprotocol / oasis-wallet-ext Goto Github PK

View Code? Open in Web Editor NEW
29.0 12.0 16.0 3.02 MB

The official non-custodial browser extension wallet for the Oasis Network.

Home Page: https://chrome.google.com/webstore/detail/oasis-wallet/ppdadbejkmjnefldpcdjhnkpbjkikoip

License: Apache License 2.0

JavaScript 86.48% HTML 0.07% SCSS 13.05% Makefile 0.41%
wallet blockchain ledger cryptocurrency non-custodial official oasis staking delegation chrome-extension

oasis-wallet-ext's Introduction

ROSE Wallet - Browser Extension

CI lint status

The official non-custodial browser extension wallet for the Oasis Network.

Introduction

ROSE Wallet - Browser Extension provides a complete solution for managing your ROSE tokens, including everything for convenient staking of your ROSE tokens.

Features

Wallet and Accounts

  • Create/Restore wallets using standard BIP-0039 mnemonic phrases
  • Multiple accounts within the same wallet
  • Import accounts from your Ledger hardware wallet
  • Import accounts directly from their private keys
  • Watch 3rd-party accounts
  • Address book of your saved accounts
  • Easily switch between different ROSE wallets that use the same ADR 0008 standard account key generation process.

Transactions

  • Send tokens
  • Delegate/Undelegate tokens
  • View transaction history
  • View staking/delegation
  • Connect your wallet to dApps and sign their transactions.

Accessibility

  • Available in multiple languages (currently, English and Chinese)

Architecture

The extension is roughly laid out this way:

  • background - the extension's background page

    • APIService (wallet info storage, in-memory transaction signing, transaction submission)
    • ExtDAppService (DApp interface, handles requests from xu-frame)
  • service-data - connections to the network and block explorer

  • popup - interactive parts

    • pages (UI, Ledger transaction signing)
    • actions (reducer action)
    • reducer
  • xu-frame - a page that DApps can load in an iframe to communicate with this extension

    • code for this page is in background/ExtUtils.js
    • Oasis SDK ext-utils handlers

Architecture Diagram

Getting started

build extension

ROSE Wallet extension repo uses git-secret to encrypt the endpoints and the api keys. So, you can't build this without creating your own config file. You should create your own config.js file in the folder. Refer to the config.example.js sample file to create your own configuration.

yarn install
yarn dev

Install and running extension

Extension's build output is placed in /dist, and you can check out this page for installing the developing extension.

dapp-connect test

  • open oasis-test-dapp
  • click connect and other button to communicate with wallet

CI pipelines (Coming soon)

yarn install --frozen-lockfile
yarn test
yarn buildProd

Preparing a Production Release Build

VERSION=`cat public/manifest.json | jq .version -r`
COMMIT=`git rev-parse --short HEAD`
rm -rf dist/
yarn install --frozen-lockfile
yarn buildProd
zip -r rose-wallet-$VERSION-$COMMIT.zip dist/

If you're actually making a new release, follow the applicable steps in the release process doc

LICENSE

Apache License 2.0

oasis-wallet-ext's People

Contributors

buberdds avatar dependabot[bot] avatar lukaw3d avatar lvshaoping007 avatar nhynes avatar peterjgilbert avatar pro-wh avatar realtimetodie avatar tjanez 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

Watchers

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

oasis-wallet-ext's Issues

yarn.lock and package-lock.json are invalid

  • How come there are two lockfiles? Remove one unless both can easily be kept authoritative
  • Both lockfiles are out of sync
yarn install --frozen-lockfile
> error Your lockfile needs to be updated, but yarn was run with `--frozen-lockfile`.

npm ci
> npm ERR! Invalid: lock file's @oasisprotocol/[email protected] does not satisfy @oasisprotocol/[email protected]
> npm ERR! Missing: @oasisprotocol/[email protected]
> npm ERR! Missing: @oasisprotocol/[email protected]
> npm ERR! Missing: [email protected]
> npm ERR! Missing: [email protected]
> npm ERR! Missing: [email protected]
> npm ERR! Missing: [email protected]
> npm ERR! Missing: [email protected]
> npm ERR! Missing: [email protected]
> npm ERR! Missing: [email protected]
> npm ERR! Missing: [email protected]
> npm ERR! Missing: [email protected]
> npm ERR! Missing: [email protected]
> npm ERR! Missing: @babel/[email protected]
> npm ERR! Invalid: lock file's [email protected] does not satisfy jest@^26.6.3
> npm ERR! Invalid: lock file's [email protected] does not satisfy mocha@^7.2.0
> npm ERR! Missing: sinon@^11.1.1
> npm ERR! Missing: [email protected]
> npm ERR! Missing: [email protected]
> npm ERR! Missing: [email protected]
> npm ERR! Invalid: lock file's [email protected] does not satisfy jsdom@^8.1.0
> npm ERR! Missing: [email protected]

Looking at e.g. @oasisprotocol/client-ext-utils in package.json

"@oasisprotocol/client-ext-utils": "0.1.0-alpha3",

wrong version in yarn.lock
"@oasisprotocol/[email protected]":

missing in package-lock.json

axios: backoff sometimes mutates original config

let newConfig = config
if(config.method === "post" && config.data){
let queryData = config.data
let variables =queryData.variables
if(variables){
if(variables.requestType){
newConfig.data.variables.requestType = ""
}
}else{
let data = queryData.replace("extensionAccountInfo", "");
newConfig = {...config,data}
}
}
newConfig.isRetrying = true

newConfig is sometimes mutated without first reassigning it to a copy. Could that be a mistake, judging by the name of the variable?

blocks #1

missing updateState for data?

this.memStore.updateState({ password: pwd })

updateSecPassword updates this data object from the store, but it doesn't call updateState with data. I understand that this isn't necessary because data is a reference to the actual object in the store anyway. but lots of other similar functions do use updateState. I think either one of these must be a mistake.

blocks #1

transaction confirmation popup: params--message synchronization

return signRequests[signRequests.length - 1];

the signature request 'message' is sent over popup url parameters, but the parsed parameters are requested over the message bus, which retrieves it from the top of a queue. how does the extension make sure these stay in sync?

i.e. is there any guarantee that the popup will receive the correct params set corresponding to the message+context by the time it loads and asks the background?

blocks #1

Notification click listener doesn't unsubscribe

let id = hash
extension.notifications &&
extension.notifications.onClicked.addListener(function (id) {
let url = getExplorerUrl() + "transactions/" + id
openTab(url)
});
let title = getLanguage('notificationTitle')
let message = getLanguage('notificationContent')
extension.notifications.create(id, {

If user creates 3 transactions (without closing the popup), and clicks on 3rd notification, then it will open 3 duplicate tabs

  • listener should probably filter if (id === id) and then removeListener
  • I would prefix id with e.g. 'txConfirmed-' + id, so adding different notifications won't cause a conflict

Uuid not necessarily universal unique id

const getUuid = () => {
return 'toast-container' + new Date().getTime() + '-' + toastCount++;
};

this is used in

and it's unclear whether an UUID is really needed and what could happen if the id field could have collisions -- whether it's sent to any servers (i'm guessing not), and what would happen if somebody w/ a separate connection to the same server(s) were to guess the uuid value. Date().getTime() is in milliseconds, so guessing it might require a few trials, but should definitely be feasible.

a comment as to the implications would be nice. or rename this if an actual UUID/GUID is not needed. if one is actually needed, then using a real RFC-4122 compliant UUID might be nice, since there are existing uuid solutions, e.g., https://www.npmjs.com/package/uuid, which could be used.

tagging #1 for possible security implications

More error handling?

(non-blocking)

I see this case where we don't explicitly handle this error.

Perhaps we should or _ prefix the param.

Deprecation: React componentWillMount

(non-blocking) componentWillMount warning.

Warning: componentWillMount has been renamed, and is not recommended for use. See https://reactjs.org/link/unsafe-component-lifecycles for details. * Move code with side effects to componentDidMount, and set initial state in the constructor. * Rename componentWillMount to UNSAFE_componentWillMount to suppress this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run `npx react-codemod rename-unsafe-lifecycles` in your project source folder. Please update the following components: %s

CI

In addition to the dependabot integration, CI and testing would be helpful.

Blocks #1

Note. I thought there was a ticket for this already.

Some devDependencies are mixed into dependencies

There's at least: node-sass, react-scripts, webpack

"node-sass": "4.14.1",
"obs-store": "4.0.3",
"prop-types": "15.7.2",
"qrcode-generator": "1.4.4",
"react": "17.0.1",
"react-circular-progressbar": "2.0.3",
"react-compound-slider": "3.3.1",
"react-dom": "17.0.1",
"react-helmet": "6.1.0",
"react-i18next": "11.8.2",
"react-redux": "7.2.2",
"react-router": "5.1.2",
"react-router-dom": "5.1.2",
"react-scripts": "4.0.1",
"redux": "4.0.5",
"safe-buffer": "5.2.1",
"valid-url": "1.0.9",
"webpack": "4.44.2",

transaction approval chain context/network indicator

image

I think we had asked to defer this, and here we are at the end of this phase of development. we don't have an indicator of what network this transaction is meant for (I think this particular one was for testnet). there's actually an unrelated "Mainnet" indicator on top, which adds to the potential to confuse.

blocks #1

UX: Recovery mnemonic is not copy-pastable in ShowMnemonic

Text isn't selectable because of

showMne = () => {
return (
<div className={"mne-container"}>
{this.state.mnemonic.split(" ").map((item, index) => {
return <p key={index + ""} className="mne-item mne-item-common">{index + 1 + ". " + item}</p>;
})}
</div>
);

p {
margin: 0;
padding: 0;
-webkit-user-select: none;
-moz-user-select: none;
-o-user-select: none;
-ms-user-select: none;
}

.App {
user-select: none;
-webkit-user-drag: none;

so e.g. this would allow selection

.mne-container,
.mne-container .mne-item {
  user-select: all;
}

or displaying it in a textarea like RevealSeedPage

Spellcheck the code (e.g. with cSpell)

By adding ./cSpell.json

{
  "version": "0.1",
  "flagWords": [
    "witch",
    "singer"
  ],
  "words": [
    "oasisprotocol",
    "bignumber",
    "bech",
    "extensionizer",
    "webusb",
    "ledgerhq",
    "grpc",
    "qrcode",
    "Identicon",
    "jazzicon",
    "reduxjs",
    "Nacl",
    "tweetnacl",
    "hdpath",
    "hdkey",
    "axios",
    "wechat",
    "passworder",

    "testmodal",
    "subres",
    "WINDOWID",
    "Progressbar",
    "backoff",

    "debonding",
    "debond",
    "unbounding",
    "unactive",
    "cointypes",
    "delegators"
  ]
}

and running

npx cspell "src/i18n/en.json" "src/**/*.js" "src/**/*.scss"

finds 288 typos (e.g. vaildatorNode, lable, UPDATA_ENTRY_WITCH_ROUTE)

Standardize and increase comments' accessibility

(non-blocking)

I think we can make more comments more consistent. In this case, I think we can rename the variable for handled to be even more explicit and skip the comment.

We also have doc comment style comments. I don't see where we use them explicitly?

axios: could removeQueue skip items that it should remove?

for (let i = 0, size = queue.length; i < size; i++) {
const task = queue[i];
if (task && task.token === configSession) {
task.cancel({ message: ERROR_TYPE.CanceRequest, config: config });
queue.splice(i, 1);
}
}

usually when you splice out elements in a loop like this you decrease i and decrease the size. here the code doesn't. that's likely a mistake. the iteration would run off the end if it removes anything

blocks #1

some data is encrypted multiple times, but password stored in memory

for example, the private keys are encrypted, then put into the data object, then encrypted.

that seems like it could be good if unlock your wallet and then walk away from your computer, maybe we would want to protect our keys if an attacker would come and look at the extension's in memory state.

but the password is also stored in the in memory state, so it wouldn't be able to defend against that.

I'm posting this to ask if any of this is unintended

blocks #1

password agreement looks disabled?

image

it's light gray, which lightening the color usually means disabled, like with the "Next" button below. but it's enabled, and in fact you must check it before continuing

blocks #1

Increased linting

(non-blocking)

I think we can increase the tooling and tighten up whitespace linting and prefix variable names with _ when unused.

Some boolean expressions will then also be more clear too.

Enable Dependabot, there are vulnerable dependencies

Blocking #1

  $ npx snyk test

- Tested 1623 dependencies for known issues, found 33 issues, 190 vulnerable paths.


  Issues to fix by upgrading:

    Upgrade @reduxjs/[email protected] to @reduxjs/[email protected] to fix
-   โœ— Prototype Pollution [High Severity][https://snyk.io/vuln/SNYK-JS-IMMER-1019369] in [email protected]
      introduced by @reduxjs/[email protected] > [email protected]

    Upgrade [email protected] to [email protected] to fix
-   โœ— Prototype Pollution [High Severity][https://snyk.io/vuln/SNYK-JS-I18NEXT-1065979] in [email protected]
      introduced by [email protected]

    Upgrade [email protected] to [email protected] to fix
#   โœ— Regular Expression Denial of Service (ReDoS) (new) [Low Severity][https://snyk.io/vuln/SNYK-JS-TAR-1536758] in [email protected]
      introduced by [email protected] > [email protected] > [email protected] and 1 other path(s)
-   โœ— Denial of Service (DoS) [High Severity][https://snyk.io/vuln/SNYK-JS-TRIMNEWLINES-1298042] in [email protected]
      introduced by [email protected] > [email protected] > [email protected]
-   โœ— Arbitrary File Overwrite (new) [High Severity][https://snyk.io/vuln/SNYK-JS-TAR-1536528] in [email protected]
      introduced by [email protected] > [email protected] > [email protected] and 1 other path(s)
-   โœ— Arbitrary File Overwrite (new) [High Severity][https://snyk.io/vuln/SNYK-JS-TAR-1536531] in [email protected]
      introduced by [email protected] > [email protected] > [email protected] and 1 other path(s)


  Issues with no direct upgrade or patch:
!   โœ— Regular Expression Denial of Service (ReDoS) [Medium Severity][https://snyk.io/vuln/SNYK-JS-BROWSERSLIST-1090194] in [email protected]
      introduced by [email protected] > [email protected] > [email protected]
    This issue was fixed in versions: 4.16.5
!   โœ— Regular Expression Denial of Service (ReDoS) [Medium Severity][https://snyk.io/vuln/SNYK-JS-COLORSTRING-1082939] in [email protected]
      introduced by @metamask/[email protected] > [email protected] > [email protected]
    This issue was fixed in versions: 1.5.5
!   โœ— Regular Expression Denial of Service (ReDoS) [Medium Severity][https://snyk.io/vuln/SNYK-JS-CSSWHAT-1298035] in [email protected]
      introduced by [email protected] > @svgr/[email protected] > @svgr/[email protected] > [email protected] > [email protected] > [email protected]
    This issue was fixed in versions: 5.0.1
!   โœ— Arbitrary Code Injection [Medium Severity][https://snyk.io/vuln/SNYK-JS-EJS-1049328] in [email protected]
      introduced by [email protected] > [email protected] > [email protected] > @surma/[email protected] > [email protected]
    This issue was fixed in versions: 3.1.6
!   โœ— Regular Expression Denial of Service (ReDoS) [Medium Severity][https://snyk.io/vuln/SNYK-JS-GLOBPARENT-1016905] in [email protected]
      introduced by [email protected] > [email protected] > [email protected] > [email protected] > [email protected] and 1 other path(s)
    This issue was fixed in versions: 5.1.2
!   โœ— Regular Expression Denial of Service (ReDoS) [Medium Severity][https://snyk.io/vuln/SNYK-JS-HTMLPARSESTRINGIFY2-1079307] in [email protected]
      introduced by [email protected] > [email protected]
    No upgrade or patch available
-   โœ— Command Injection [High Severity][https://snyk.io/vuln/SNYK-JS-LODASHTEMPLATE-1088054] in [email protected]
      introduced by [email protected] > [email protected] > [email protected] > [email protected]
    No upgrade or patch available
!   โœ— Improper Certificate Validation [Medium Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-1059081] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
!   โœ— Out-of-Bounds [Medium Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-535498] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
-   โœ— NULL Pointer Dereference [High Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-535500] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
!   โœ— NULL Pointer Dereference [Medium Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-535502] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
-   โœ— Out-of-bounds Read [High Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-540956] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
!   โœ— Out-of-bounds Read [Medium Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-540958] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
!   โœ— Uncontrolled Recursion [Medium Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-540964] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
!   โœ— Denial of Service (DoS) [Medium Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-540978] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
!   โœ— Denial of Service (DoS) [Medium Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-540980] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
!   โœ— Out-of-bounds Read [Medium Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-540990] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
!   โœ— NULL Pointer Dereference [Medium Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-540992] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
!   โœ— NULL Pointer Dereference [Medium Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-540994] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
-   โœ— Out-of-bounds Read [High Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-540996] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
!   โœ— Out-of-Bounds [Medium Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-540998] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
-   โœ— Use After Free [High Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-541000] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
!   โœ— Out-of-bounds Read [Medium Severity][https://snyk.io/vuln/SNYK-JS-NODESASS-541002] in [email protected]
      introduced by [email protected]
    No upgrade or patch available
!   โœ— Regular Expression Denial of Service (ReDoS) [Medium Severity][https://snyk.io/vuln/SNYK-JS-POSTCSS-1090595] in [email protected]
      introduced by [email protected] > [email protected] > [email protected] and 76 other path(s)
    This issue was fixed in versions: 7.0.36, 8.2.10
!   โœ— Regular Expression Denial of Service (ReDoS) [Medium Severity][https://snyk.io/vuln/SNYK-JS-POSTCSS-1255640] in [email protected]
      introduced by [email protected] > [email protected] > [email protected] and 76 other path(s)
    This issue was fixed in versions: 8.2.13, 7.0.36
!   โœ— Open Redirect [Medium Severity][https://snyk.io/vuln/SNYK-JS-URLPARSE-1533425] in [email protected]
      introduced by [email protected] > [email protected] > [email protected] > [email protected] and 1 other path(s)
    This issue was fixed in versions: 1.5.2
!   โœ— Regular Expression Denial of Service (ReDoS) [Medium Severity][https://snyk.io/vuln/SNYK-JS-WS-1296835] in [email protected]
      introduced by [email protected] > [email protected] > [email protected]
    This issue was fixed in versions: 7.4.6, 6.2.2, 5.2.3

nit: unnecessary code dup

not sure if it's just normal javascript boilerplate style, but the code fragments

ConfirmModal.show({
title, content,
confirmText,
showClose: true,
onConfirm: this.onModalConfirm,
})

ConfirmModal.show({
title, content,
confirmText,
showClose: true,
onConfirm: this.onModalConfirm,
})

ConfirmModal.show({
title, content,
confirmText,
showClose: true,
onConfirm: this.onModalConfirm,
})

are identical, so can be de-duplicated and hoisted out to follow the switch. is there a reason why we shouldn't?

this is not a security issue, just something i noticed while auditing. de-dup should reduce code size even after minification, so saves a little bandwidth and improve page load time slightly etc.

unescaped url parameters

this.popupId = await this.dappOpenPopWindow('./popup.html#/approve_page?isUnlocked=' + isUnlocked
+ "&siteUrl=" + siteUrl
+ "&siteIcon=" + icon,

some parameters like the icon parameter can contain & which can trick later parsing of those parameters. for example, an icon like https://example.com/icon.png?a=1&isUnlocked=1 could clobber the intended isUnlocked parameter.

related: #14 is a separate objection to url parameters

blocks #1

Refactor to not store password in memory

Related to #31

Searching for getStore().password, it seems that password is stored in memory so that it can be used in the following methods:

  • createAccount
  • checkPassword
  • addHDNewAccount
  • addImportAccount
  • addLedgerAccount
  • addObserveAccount
  • setCurrentAccount
  • changeAccountName
  • deleteAccount
  • getMnemonic
  • getCurrentPrivateKey

How different scenarios can be handled without keeping password:

  • checkPassword: use hash to compare, or just try to unlock
  • addImportAccount, changeAccountName, ..: asymmetrically encrypt, and only keep public key in memory for continuously encrypting changes
  • getCurrentPrivateKey: ask for password

Simplify getQueryStringArgs

export function getQueryStringArgs(url) {
let qs = url || ""
let paramSplit = qs.split("?")
let paramStr = ''
if (paramSplit.length > 1) {
paramStr = paramSplit[1]
}
var args = {};
var items = paramStr.length > 0 ? paramStr.split("&") : [],
item = null, name = null, value = null;
var len = items.length;
for (var i = 0; i < len; i++) {
item = items[i].split("=");
name = decodeURIComponent(item[0]);
value = decodeURIComponent(item[1]);
if (name.length > 0) {
args[name] = value
}
}
return args;
}
export function getOriginFromUrl(url) {
var origin = new URL(url).origin;
return origin
}

Similar to using new URL(url).origin for getOriginFromUrl
you can use new URL(url).searchParams

axios: expectIndex assigned and not read

expectIndex = variables && variables.requestType && variables.requestType === "extensionAccountInfo" || ""
let query = queryData.query
let publicKeyIndex = query.indexOf("publicKey")
if(publicKeyIndex !== -1){
requestSession = query.slice(0,publicKeyIndex)
}else{
requestSession = query
}

assigns expectIndex, never to access it again. that couldn't be right

and setting it to a value that's sometimes boolean, sometimes empty string

blocks #1

Internal audit top level issue

when we start the audit, please mark all issues found during this audit as blocking this one. this issue should not be marked as complete until all blockers are resolved (fixed or deferred, etc).

general guideline / things to look for:

  • identify potential attack surfaces -- e.g., user input (can users attack anything other than their own account?), unauthenticated JS loading, server auth / MITM potentials (web proxy to futz with messages), not all are nec'y valid (depends on threat model)
  • input validation -- data types, value ranges, regex matching/filtering, XSS, SQLi, etc; how errors are handled/reported
  • unexpected event ordering / state transitions -- what are the sources of events? are event-order validation needed?
  • privilege minimization: can the extension's permissions be further restricted (removed, made optional)?
  • test coverage

(please edit to add general pointers/topics).

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.