Security

Application code

The 21RISK application is a Sveltekit project, written in typescript . When the application is built, the following outputs are generated:

Static files HTML and JS files, ready to be served directly to the end-users without modification.
Run-time javascript server Answers HTTPS requests, with JSON/HTML.

All code at 21RISK is committed to a single repo, making it possible to track all code-changes. We use Git + Github to manage commits, issues, PR’s + more.

Backup of code

At 21RISK we use git for distributing our code-base. Git is a distributed version control system used primarily to track and manage changes in files. It is widely used in coordinating work among developers for collaborative software development. Git's distributed nature allows each developer's copy of the code to act as a repository, housing a complete history of changes. This enhances operational efficiency and provides robustness against server failures.

Git's key features include speed, data integrity, support for non-linear development workflows, and a staging area where changes can be formatted and reviewed before final commitment. It is also a free and open-source software, distributed under the GNU General Public License.

GitHub, a web-based hosting service for Git repositories, expands upon the capabilities of Git by providing added features such as bug tracking, task management, and continuous integration and deployment pipelines. It also provides access control, enabling the management of who has access to the repositories and to what extent.

Restore the codebase

One of the most powerful features of Git, and central to its function as a version control system, is its ability to revert back to previous states of a project. Since every commit to the repository is tracked, we have a comprehensive history of every change made to the codebase, complete with commit messages describing what changes were made and why.

Restoring the code to a specific point in time can be as simple as checking out a previous commit. For example, if a new feature introduced a bug, or if we simply want to review the state of the project at a certain point in the past, we can use the 'git checkout' command followed by the unique identifier of the commit we want to return to.

This feature not only allows us to undo changes and correct errors with relative ease, but also provides a detailed audit trail of the evolution of our project. This can be instrumental for tracking the origin of issues and understanding the context in which changes were made.

Note

If you need a video to demonstrate how we can restore the codebase, contact support@21risk.com

How new code is committed to our repo

In 21RISK we are dedicated to not only write code that works, but also code that is secure. Code that works does not mean that code is secure. Unit testing our functions to make sure they not only satisfy functionality, but are also secure. This is especially true when we develop new features, or for other reasons introduce new code.

When a new feature is developed, or bug fixes are written, our approach is to:

  1. Create a new branch
  2. Commit code-changes to the branch
  3. Push the branch to Github, and wait for tests to run
  4. Ask for review by colleague (no code is merged, unless reviewed by minimum one other programmer).
  5. Rebase into master, if all tests are green and no other issues are found.

From a security perspective the manual review is focused very much on security, making sure that we not only write code that accomplishes the task, but is also secure.

Automated testing at 21RISK

At 21RISK we embrace automated testing, and use a minimal amount of manual testing. Our elaborate testing ensures software of high quality, and makes it possible for us to push code-updates on a daily basis. The testing suite also makes our codebase robust towards refactoring.

Unit-testing

All projects in 21RISK are written with hundreds of unit-tests. Unit tests are ideal for pure functions, that don’t have any dependencies. They are fast to execute, and serve as documentation of expected behavior. By doing this they also help to protect against regressions, when refactoring happens.

Integration-testing

Integraion tests in 21RISK are essentially written like this:

  • Start a clean local database only for testing
  • Seed database in a specific state
  • Mutate/Query the application
  • Make sure mutation + queries respond with expected answers
  • Make sure the database is in the expected state

We have integration tests for all queries and mutations in 21RISK, and are very important to cover that the application itself is actually working, without taking the UI into account.

The integration tests are very important for authorization at 21RISK, where we test that given roles/users can only access/mutate related resources.

E2E tests

We use Playwright to perform e2e tests.

At 21RISK we write e2e test to make sure we can interact with the GUI, and get the expected results. E2E tests are especially good to make sure that core-beahviour actually works, dialogs can be closed, error messages are shown etc.

Note

Our focus when testing is currently Chromium and the safari mobile browser.

Visual testing

To protect against visual “bugs”, like colors not working or buttons that are missing, we utilize visual testing.

Visual testing is done by using our e2e framework to navigate to a specific page in the application, with a specific state, and submit the DOM tree to our visual test provider Percy. Percy then renders the DOM in a Chrome, Firefox and Safari website. When the DOM is sent to Percy, it’s called a snapshot. Each snapshot has a unique name. The power comes when making a new PR, and snapshots are compared to their previous version.

If any changes are observed by the image-comparing algorithm at Percy, a manual review is needed to make sure no breaking changes are present. From there changes can be marked as “safe” or “require” changes before a merge can be done.

Email-testing

Testing emails in 21RISK is done by using our e2e test framework to make our application send a given email. To catch the email, we use testmaill.app to receive the email. The testmail.app API then makes it possible for us to fetch the HTML body, and render it in a browser. We then send this snapshot to Percy, to make sure the emails “keep” looking the same.

Dependencies and NPM

Making sure that we use the correct dependencies is one of the most critical aspects of keeping a Node.js application secure. Before integrating a new dependency we always make sure to critically evaluate the need for the dependency in the first place. If there is a need for the dependency, keeping the dependency up to date is the second-most important thing.

In production we use the npm ci command to install dependencies. This makes sure that we use the package-lock.json file to make sure the exact versions of dependencies are installed, that was committed to version control.

Up-to-date dependencies

We use 3 processes for detecting old/vulnerable dependencies in our repo:

Snyk.io Automated vulnerability detection of npm packages and javascript
NPM outdated A built in feature in NPM, that helps check dependencies
NPM audit A feature that helps check for vulnerabilities in the project

As of November 22th, 2023 NPM audit found 3 moderate vulnerabilities in our dependencies.

Note

We are in communications with Sendgrid about the incoming fix for the axios vulnerability.