Giter VIP home page Giter VIP logo

snjs's Introduction

SNJS

lerna

SNJS is a client-side JavaScript library for Standard Notes that contains shared logic for all Standard Notes clients.

Introduction

SNJS is a shared library for use in all Standard Notes clients (desktop, web, and mobile). Its role is to extract any business or data logic from client code, so that clients are mostly responsible for UI-level code, and don’t have to think about encryption and key management, or even authentication or storage specifics. Extracting the code into a shared library also prevents us from having to write the same critical code on multiple platforms.

The entry point of SNJS is the SNApplication class. The application class is a complete unit of application functionality. Theoretically, many instances of an application can be created, each with its own storage namespace and memory state. This can allow clients to support multiple user accounts.

An application must be supplied a custom subclass of DeviceInterface. This allows the library to generalize all behavior a client will need to perform throughout normal client operation, such as saving data to a local database store, saving key/values, and accessing the keychain.

On Web platforms SNJS interacts with sncrypto to perform operations as mentioned in the specification document. This includes operations like key generation and data encryption.

SNJS also interacts with a Standard Notes syncing server, which is a zero-knowledge data and sync store that deals with encrypted data, and never learns of client secrets or sensitive information.

Installation

yarn add snjs

Integrating in module environment

import { SNApplication } from 'snjs';

Integrating in non-module web environment

<script src="snjs.js"></script>
Object.assign(window, SNLibrary);

Building

  1. yarn install --pure-lockfile
  2. yarn start to start Webpack in development mode (watches changes), or yarn build to create dist files.

Tests

E2E Tests

Ensure you have Docker and Docker Compose installed before running tests.

From the root of the repository, run:

# Starts browser-navigable web page
yarn run start:e2e:mocha

# Starts backend servers
yarn run start:e2e:docker

Then choose between the following run options:

  • Run tests in the command line:

    yarn run test:e2e:dev
    
  • Run tests in the browser: Open http://localhost:9002/packages/snjs/mocha/test.html.

Unit Tests

From the root of the repository, run:

yarn run test:unit

Help

Join the #dev channel in our Slack group or Discord for help and discussion.

snjs's People

Contributors

amanharwara avatar antsgar avatar arielsvg avatar dependabot[bot] avatar funktionzero avatar gorjan5sk avatar johnny243 avatar karolsojko avatar laurentsenta avatar moughxyz avatar myreli avatar radko93 avatar rsesek avatar standardci avatar standarius avatar theodorechu avatar vardan-arm avatar

Stargazers

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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

snjs's Issues

Server history support

Consumes this API: standardnotes/syncing-server#60

Session history and Server history should be combined into a single HistoryService interface. Clients should implement a single History menu that separates into two sections: Server History, and Session History. Selecting a revision from either section should present the same UI.

cc @radko93 @baptiste-grob @johnny243

Consider using AEAD encryption like AES-GCM in version 004

In the current implementation, all the user data are encrypted with AES-CBC that is vulnerable to the Padding Oracle attack.

We could use AES-GCM known as more secure than AES-CBC by providing authenticated encryption (AEAD).
ChaCha20-Poly1305 could be a great alternative especially when the device does not support the AES instruction set (that can be the case for smartphones).

Keychain interface should be namespaced

As part of device interface/protocol service. Currently it is not equipped to handle multiple applications instances writing to the keychain. The last application to write replaces any previous values.

make DEFAULT_SYNC_SERVER first party too.

While self hosting both web and sync server , i want treat sync server (DEFAULT_SYNC_SERVER) defined in web's .env as first party in isUrlFirstParty

export function isUrlFirstParty(url: string): boolean {
try {
const { host } = new URL(url);
return host.startsWith(LocalHost) || APPLICATION_DEFAULT_HOSTS.includes(host);
} catch (_err) {
return false;
}
}
and/or extend TRUSTED_FEATURE_HOSTS and APPLICATION_DEFAULT_HOSTS list.

Sort order

Lately, I've noticed that the sort order of special characters have changed somethat in Standard Notes. For example, - (dash) is sorted beferore ! (exclamation mark) on views in the Android app but not on Standard Notes desktop. Also, the sort order of notes has changed. & (amperstand) is sorted before # (hastag) when it recently was the other way around.

So now I hava a feature request: Keep the same sort order (preferably the standard ASCII sort order - see link) for views and tags on all platforms (Android, desktop, and web). Also, once a sort order is established, don't change it from one update to another. Some of use use letters and special characters to organize our notes, like in a bullet journal, and would much appreciate if any changes to the sort order is avoided as much as possible.

Refactor AlertService

I need to change the SNAlertService to support the addition of a blocking banner that shows a message, for when we upgrade the protocol version.
Because this will be a breaking change, I think it's a good opportunity to refactor the whole service.
Right now each platform (web/desktop & mobile) needs to re-implement the service methods so I think it would make more sense to only use an interface and require clients to give an implementation when constructing the SNApplication instance. Something like this:
The current file is here: https://github.com/standardnotes/snjs/blob/004/lib/services/alert_service.ts
And this is a basic version of where I'd like it to be:

const enum ButtonType {
  Info = 0,
  Danger = 1,
}

type DismissBlockingDialog = () => void;

type AlertService = {
  confirm({
    text,
    title,
    confirmButtonText,
    confirmButtonType,
    cancelButtonText,
  }: {
    text: string,
    title?: string,
    confirmButtonText: string,
    confirmButtonType?: ButtonType,
    cancelButtonText?: string,
  }): Promise<boolean>;
  alert(text: string, title?: string, closeButtonText?: string): Promise<void>;
  blockingDialog(text: string): DismissBlockingDialog
}

I don't feel strongly about this proposed API, aside from two things:

  • I need some way to instruct the client to show a blocking dialog and for SNJS to be able to dismiss it as well.
  • I'd like for the confirm method to not reject when cancelling and instead resolve with a negative value. Otherwise it leads to awkward code like this:
let didConfirm = true;
await this.alertService!.confirm(
  `Are you sure you want to delete ${itemsData.length} ${noun}?`
).catch(err => {
  didConfirm = false;
});

Which would look better like this:

const didConfirm = await this.alertService!.confirm(
  `Are you sure you want to delete ${itemsData.length} ${noun}?`
);

What do you think? @mobitar @radko93

Untagged notes view

I propose the introduction of an "Untagged notes" view that helps to identify any notes that aren't tagged. In my workflow untagged notes shouldn't exist. Usually, they're created by mistake and they quickly drown in the sheer number of notes. It would be very helpful to have a view to quickly identify and fix these notes.

Import creates tag with duplicate name in Windows app

Describe the bug
Importing a note with the same tag name as an existing tag creates a unique tag with the same name in Windows app.

To Reproduce
Steps to reproduce the behavior:

  1. Create a tag and attach it to a set of notes.
  2. Import notes that have the same tag name. I did this by tagging notes in Evernote, exporting to .enex, then converting to an import file for Standard Notes.
  3. A new unique tag with the same name is created.
  4. There are now two tags with the same name but with different sets of notes.

Expected behavior
If the tag name matches one already in Standard Notes, either attach the imported notes to the existing tag or give an option to merge with the existing tag or create a new tag with a new name.

Desktop (please complete the following information):

  • OS: Windows 10
  • Browser Chrome
  • Version 89.0.4389.90 (Official Build) (64-bit)

Key derivation returns incorrect value

Hi,

I have debugged the client side sign-in flow by setting breakpoints in the JS to see the various function calls and their results. I have recreated the code in go, using argon2 from the standard library, and copying the parameters in your code.

My test is a sign-in using:
identifier: [email protected]
password: debugtest
and a nonce returned from your server: 2c409996650e46c748856fbd6aa549f89f35be055a8f9bfacdf0c4b29b2152e9

The sha256 hash source is therefore:
[email protected]:2c409996650e46c748856fbd6aa549f89f35be055a8f9bfacdf0c4b29b2152e9
That hashes to:
7129955dbbbfb376fdcac49890ef17bc1b4f73dad21c480f0b28a36849a7459e
You reduce to 128 bit, 32 bytes being:
7129955dbbbfb376fdcac49890ef17bc

password: debugtest
hash: 7129955dbbbfb376fdcac49890ef17bc
iterations: 5
memory: 65M
parallel: 1 thread
keyLength: 64 bytes

I get the resulting argon2id hash:
7cf34ec893c9b36f455e594280039b7557c1b51067eb2254dd6eb2666b7fd48fe7b47840c731cdc941e56dbb2d8f30c66428e472eacc1b2809bf7ab9bb3aeb58

But your code produces:
2396d6ac0bc70fe45db1d2bcf3daa522603e9c6fcc88dc933ce1a3a31bbc08eda5eb9fbc767eafd6e54fd9d3646b19520e038ba2ccc9cceddf2340b37b788b47

I have tried with the reference tool: https://github.com/P-H-C/phc-winner-argon2 and it confirms my result:

echo -n "debugtest" | ./argon2 7129955dbbbfb376fdcac49890ef17bc -t 5 -m 16 -p 1 -l 64 -id
Type:		Argon2id  
Iterations:	5  
Memory:		65536 KiB  
Parallelism:	1  
Hash:		7cf34ec893c9b36f455e594280039b7557c1b51067eb2254dd6eb2666b7fd48fe7b47840c731cdc941e56dbb2d8f30c66428e472eacc1b2809bf7ab9bb3aeb58  
Encoded:	$argon2id$v=19$m=65536,t=5,p=1$NzEyOTk1NWRiYmJmYjM3NmZkY2FjNDk4OTBlZjE3YmM$fPNOyJPJs29FXllCgAObdVfBtRBn6yJU3W6yZmt/1I/ntHhAxzHNyUHlbbstjzDGZCjkcurMGygJv3q5uzrrWA
0.163 seconds
Verification ok

I'm almost certain I am doing something wrong! Please could you check and let me know where I'm going wrong (hopefully) or if your code/dependency is incorrect (hopefully not)?

Add duplicate_of support

The new syncing server uses a top-level duplicate_of field that is equal the to the UUID of the item this item is a duplicate of. Currently production snjs implements this via item.content.conflict_of, which is encrypted to the server. In the new behavior, we want to move this value up to the main level (item.duplicate_of).

The server uses this to determine how to handle revision history for items that have been conflicted.

cc @karolsojko

Test suite, CI

It would be really useful if this library's test suite was available for continuous integration environments, but it seems that the browser dependency makes this difficult. In particular, it would be a great tool for testing a StandardNotes backend implementation.

the problem

At the moment, it seems that the test suite can only be run by:

  • running node ./test-server.js
  • open a browser tab to localhost:9001

The dependence on a browser makes it difficult to use in continuous integration.

some affected projects

There are a few server-side implementations, in addition to the official syncing-server such as:

Some of these have test suites, but does it even matter if the client does not work?

ideas for solution

Could the test suite be set up to use node? If the execution environment shifted to node, then it would be easier to run the test suite along the side of any of the aforementioned syncing servers.
A docker image and one location to set a server URL in the test would also be really nice to have.

Search Improvements

Currently Standard Notes has a simple searching protocol and there are minor improvements that would be:

  1. all lowercase text would search case insensitive and diacritics.

That is wolf would match Wolf wOlf, WOLF, WOLF and wölf

  1. An upper case letter anywhere would search directly for that word or sequence.

  2. search for 'wolf' would return notes that only contain wolf

  3. search for ''wolf'' (double single quotes) would search for 'wolf' (that means that quotes are nestable)

In other words:

All lower case we do a fuzzy search, when we add an upper case letter we stop searching as fuzzy.

I would be more happy to implement this feature if there is interest for this.

Can API version be in header too?

Hello, I want to know if it's possible to add the api version (eg. 20200115) in the HTTP header too?

My main motivation is that I develop an alternative server that aims to be lightweight and portable and getting the API version from the request's body is not handy.
As my server is developed in a typed language, the JSON is parsed at the end of the HTTP stack in the controllers' handlers. So I can't have the API version in the middlewares level to apply special logic like revoke JWT tokens if the client supports sessions.

Questions about two parts of the code

What is a Room SN|Component?

I'm trying to migrate Flame Enigma to a setup where each secret is in a seperate note but is collectively 'the passwords database'.
This provides several key benefits that aren't easily replicatable within a single page note.

  1. Tagging and organizing secrets like you would any other note
  2. Easy searching on mobile (even with no extension installed)

However this requires there to be a long running process that is independent of each note's editor so that things such as synchronization can still occur without having to remember to open up the effected notes.

Why does markDone() clear a function after executing it once?

(This is withing the migration system).

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.