Web UI for Artemis source code security scanner.
- 🛡️ Artemis UI
If you already have prerequisites installed, after cloning this project you can get up and running by:
npm ci
npm start
This should install all project dependencies, start the app in development mode, and open a new web browser tab for http://localhost:3000.
Note: The UI will "hot reload" in development mode when code changes are made.
Once you have the project running, you will want to customize it to meet your needs. This includes:
- Review the
.env.*
files in the project root directory and modify to meet your requirements - Populate the variables in the
nonprod.mk
andprod.mk
Makefiles to match your deployment - Customize the
homepage
field inpackage.json
to match your deployment. Refer to create-react-app documentation - Review and customize files in the
src/custom
directory to meet the requirements of your application, such as customizing the application logo, page footer, welcome message, and data export message- Search for "REPLACE ME" strings
- Populate the
public
directory with any files you want to add, such asfavicon.ico
The following steps describe how to bootstrap this project for development on a Mac .
For Windows or Linux development, comparable steps exist, omitting certain utilities like Homebrew.
- Install a terminal emulator, like iTerm
- Install an IDE with support for ECMAScript, React, Redux language features or plugins, such as Visual Studio Code
- Install Homebrew, a package manager for many of the tools we will be using
- Install Node via NVM
- Install Python 3
- Install a container management tool, such as Docker Desktop
- Install Git. This can be accomplished by installing Apple XCode from the App Store, via Homebrew, or directly
- Clone this GitHub project
- Install AWS CLI
- Install Terraform via Homebrew or directly
- Install pipenv via pip3
pip3 install -g pipenv
To develop natively in the shell:
- Install Node Version Manager (nvm)
brew install nvm
- Instead of installing node directly for your user or globally, I like to use nvm to manage node versions
- This allows you to maintain multiple sandboxed versions of node in order to migrate code to newer versions
- Note: Ensure you follow any additional install instructions to create a nvm working directory and add nvm path to your current shell
- Useful nvm commands:
nvm ls
- list currently installed and available versions of Node.js in multiple streams (stable, unstable, etc.)nvm install [version|--lts]
- install a specific Node.js version or latest LTS releasenvm use [version]
- use a particular version of Node.jsnvm alias default [version]
- make a particular version of Node.js the defaultnvm uninstall [version]
- uninstall version of Node.js
- Install Node.js
nvm install --lts
- This will install the latest LTS release of Node.js and make it the default if you don't have other versions installed
- It's recommended to use an LTS release when developing an enterprise app so that you receive maintenance updates for this version for the longest duration
- Node install includes facilities for package management (npm) and for running node package binaries (npx)
- Note: The Node version of the project is maintained in the file .nvmrc
- Update NPM to latest version
npm install -g npm@latest
- Note: you may need to do this periodically to update to latest npm and also rev the version in Dockerfile.dev
- If you update your Node version, such as via
nvm install new_version --reinstall-packages-from=old_version
, so may need to reinstall the latest version of NPM
- If you update your Node version, such as via
- Install jq (command-line JSON parser), used in Makefile for parsing
package.json
)brew install jq
- Install linters used by project that aren't covered by NPM:
brew install hadolint
- Install all project dependencies
npm ci
ormake install
To develop in a Docker container: This will run the UI webserver in a container.
- Build the container and run it:
make run
If you need to run utilities for linting, code checking, or auditing dependencies, run the container shell using make exec
and then run the commands from this shell (see Scripts section below).
In the project directory, you can run:
npm start
oryarn start
ormake start
Runs the app in development mode.
This should also open a new web browser tab for http://localhost:3000. The page will reload if you make edits.
You will also see any lint errors in the console.npm test
oryarn test
ormake test
Launches the test runner in interactive watch mode.npm run build
oryarn build
ormake build|dist|all
Builds the app for production to thebuild
folder.
It correctly bundles React in production mode and optimizes the build for the best performance. The build is minified, and the filenames include the hashes, ready for deployment.npm run lint
ormake lint
Run code linting (ESLint).npm outdated
ormake outdated
View out-of-date packages/libraries.npm audit
oryarn audit
ormake audit
Audit packages/libraries for vulnerabilities.npm run prettier-check
ormake check
Run code standards check (prettier). Outputs results but doesn't make changes to source files.npm run prettier-write
ormake fix
Run code standards fix (prettier). Modifies source files to match standards.make precommit
Runs all checks: lint, audit, prettier-check.npm run extract
ormake extract
I18N: Uses LingUI to extract messages from source code into message catalogs (GNU gettext .po files). Messages will be written tolocales/en/messages.po
.npm run compile
ormake compile
I18N: Uses LingUI to compile message catalogs into a messages.js file that can be read by the application for text translations.- 🛑 DO NOT run
npm run eject
oryarn eject
This project and its dependencies are currently managed bycreate-react-app
choices for WebPack.
Running eject will disconnect this so that WebPack, Babel, ESLint, and other configs can be configured directly.
This is a one-way operation, once you eject you can't go back!
The following browser plugins help you debug/profile your React/Redux code:
- React Developer Tools
- Redux DevTools
The following IDE plugins are for VSCode:
- EditorConfig for VS Code
- ESLint
- hadolint
- Markdownlint
- Prettier
The following IDE settings apply to VSCode.
Change the following settings:
- ENABLE Editor format on save
editor.formatOnSave
- Change default MacOS terminal app
terminal.external.osxExec
toiTerm.app
The following settings should have defaults we want (no changes required):
- Prettier settings (these can also be configured in
prettier.config.js
file in the project root)- Print width: 80 characters
- Tab Width: 2
- Use Editor Config: true (checked)
- Trailing Comma: es5
- Use Double Quotes
- End lines with Semicolon
- Use Tabs: true (checked)
- ESLint ENABLED
- ESLint formatter
eslint.format.enable
DISABLED
- ESLint formatter
- TypeScript formatter
typescript.format.enable
ENABLED
-
- If using VSCode IDE, you essentially just need to add the following to the
configurations
section of yourlaunch.json
file:
{ "name": "Chrome", "type": "chrome", "request": "launch", "url": "http://localhost:3000", "webRoot": "${workspaceFolder}/src", "sourceMapPathOverrides": { "webpack:///src/*": "${webRoot}/*" } }
- You can modify the
url
value to reference a subpage if you want the debugger to launch that subpage
- If using VSCode IDE, you essentially just need to add the following to the
-
- If using VSCode IDE, you essentially just need to add the following to the
configurations
section of yourlaunch.json
file:
{ "name": "Debug CRA Tests", "type": "node", "request": "launch", "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts", "args": ["test", "--runInBand", "--no-cache", "--watchAll=false", "TESTNAME"], "cwd": "${workspaceRoot}", "protocol": "inspector", "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "env": { "CI": "true", "DEBUG_PRINT_LIMIT": "100000" }, "disableOptimisticBPs": true }
- Replace TESTNAME with the name of the test you want to run, e.g.
HiddenFindingDialog
DEBUG_PRINT_LIMIT
environment variable will print more lines of DOM output if a test fails, helping you better identify why your test didn't passCI=true
will stop after running the test instead of re-running the test(s) when files change- If running your tests from the shell, this test run equates to
CI=true DEBUG_PRINT_LIMIT=100000 npm run test TESTNAME
- If using VSCode IDE, you essentially just need to add the following to the
-
[Debugging Formik]
- Formik form state can be viewed/debugged using the React Developer Tools. Look for the component named
FormikContext.Provider
in the React Components tab of the browser debugger.
- Formik form state can be viewed/debugged using the React Developer Tools. Look for the component named
Project layout is based on Create React App standards for a React project.
- Images, manifest, and index files live in the
public
directory - All code lives in the
src
directory- Within
src
, API calls (business logic) reside inapi
folder app
contains "global app things" like the root store, root reducer, root saga, Navbar that is on every page, etc.components
contain re-usable components that have no data model tied to them, like data tables (e.g.EnhancedTable
), custom form fields, etc.features
contains code for particular types of data in the app (such as notifications, scans, users, etc.). This may or may not also contain a component for using this type of datalocale
contains app message catalogs. These are not edited manually but are created through build automationpages
contains SPA (single-page-application) pages, such as theMainPage
(where you can create/view scans), andResultsPage
where you view results for a single scanutils
contains utility functions (not components). For example, functions for formatting dates in a particular stylecustom
contains any internal customizations, for example, support for a metadata schema
- Within
Tests should be created in the same directory as the code it tests and have the same name + "test". For example, a test for App.tsx
is in the same directory as App.tsx
and named App.test.tsx
.
- Create a new environment in
terraform/environments
, copying the example and modifing as needed. - Deploy the Terraform:
terraform -chdir=terraform/environments/ENV apply
- Update
ENV.mk
, whereENV
matches the Terraform environment name. - Install the
npm
packages:npm install
- Deploy the UI to S3:
make deploy
This project is using the following checks for code style and standards:
- ESLint - JavaScript linting
- Prettier - JavaScript code style
- NPM Audit - JavaScript dependency vulnerability auditing
- Hadolint - Dockerfile linting
- Markdownlint - Markdown linting
- Snyk: 10 React Security Best Practices - Good overview of security considerations when writing a React app
- Snyk: 10 npm Security Best Practices - Good recommendations for using npm in a project more securely
- Any other standards/best practice guides related to the current application stack (JavaScript, React, Redux, etc.)
- Think in React
- Composition over inheritance
- Keep components small and function-specific
- Avoid creating new components to minimum required
- Follow the "rule of 3" when abstracting a reused piece of code into a utility function or separate component
- Capitalize component names
- Use React Hooks
- Use Yup for validation (both user input and data returned from APIs)
- Use Redux Sagas for side effect management
- This also forces you to think in terms of using hooks for effect management, instead of
await dispatch(...)
and responding to blocking state change calls, respond to state changes inuseEffect
hooks
- This also forces you to think in terms of using hooks for effect management, instead of
- Use CSS in JavaScript
- Lists should have keys
Dates/times in API fields should use UTC time zone and ISO 8601 format. It is up to the UI to "translate" that time so that the user always sees dates and times displayed in THEIR time zone. This is accomplished by using Luxon library for date/time management. At the moment, all dates/times are just being displayed in the time zone of the user's browser (usually the user's OS), which doesn't really require a library like Luxon. However, in the future, we may allow the user to set the time zone they want dates/times displayed in, and this will require Luxon support. Follow all existing code patterns where Luxon is being used for date/time management.
All text strings displayed in the UI should be handled so that they can be translated at a later time. We are currently using the LinguiJS library to accomplish this (refer to their usage documentation).
- Basic usage
- Use
<Trans>>...</Trans>
as a component child, e.g.<div><Trans>This is marked for translation</Trans></div>
- Use
i18n._(t``...``)
for text in component attributes, e.g.<Tooltip title={i18n._(t``Translated Tooltip Text``)}></Tooltip>
- Use
- Design for translation
- Different languages have different sentence structure, so don't concatenate text strings to form a sentence, e.g.
"this " + "breaks " + "translation"
- Design with space in mind: some languages may have longer or shorter representation for a piece of text, so allow elements to grow/shrink accordingly
- Different languages have different sentence structure, so don't concatenate text strings to form a sentence, e.g.
UI should be designed to support accessibility features. This includes but is not limited to:
- You can navigate to items on the page via keyboard alone
- Color should not be used alone to represent an idea, but should be accompanied by other descriptive text and/or icons
- Adjacent colors used on the page should be easily distinguishable (imagine viewing the page in black-and-white and whether the two colors would be distinguishable)
- Text/content on the screen should be resizable
- Use proper document elements and hierarchy for elements (e.g. use
<header>
,<footer>
,<h1>
, ... as they are designed to be used. Prefer to use specific element types<button>
over a genericdiv
with button styling) - Use
wai-aria
(also known as justaria
) attributes where applicable. Follow any specific guidance provided in Material UI documentation for components (such as this) - Display temporary status items like alert for a long enough duration for them to be read or provide an option for the user to close them
For a complete reference, refer to the Web Content Accessability Guidelines (WCAG).
- Follow Material design standards
- E.g. Main action buttons have additional styling (icons, color), whereas secondary buttons (dialogs, "Cancel" are unadorned)
- Don't create designs that require use of tabs-within-tabs or dialogs-within-dialogs
- If you are in a dialog and need to confirm an action "Delete this item?", replace the existing dialog content with this content, don't create a new dialog
- Don't hide data unless necessary (for clarity or screen real-estate)
- E.g. if displaying user information in a list, also list null/blank/undefined items so it's clear these were unset
- Omit data an average user doesn't need to perform their tasks
- Destructive actions should have user confirmation (e.g. confirm a user wants to remove an item before removing it)
- Features should be tested (AND include automated tests where possible)
- Create mocks in
src/api/server.ts
for local dev testing - Also test against real Artemis APIs/data
- Create mocks in
- Ensure the application version is updated with each change (in package.json and package-lock.json). Use semver.
- PR pushes should not fail automated checks
- All new and existing tests should PASS
The following technologies are used in the production of this UI:
- Axios - HTTP request client
- AutoSuggest-Highlight - Highlight matched characters in form Autocomplete field
- Formik (+Formik-Material-UI) - React form management
- Jest - Testing framework
- LingUI - Internationalization
- Luxon - Date/time management
- Material-UI - Design system and components for React
- MirageJS - API mocking library for development and testing
- Nano ID - A secure, unique string ID generator, used for "server-side" mocking of unique ids
- Query-String - Parse and stringify URL query strings
- Random-Material-Color - Generation of a random color compatible with Material-UI palette
- React
- React-Copy-To-Clipboard - Copy content to the clipboard
- React-Draggable - Component for making elements draggable (such as dialog windows)
- React-Redux (+Redux-Toolkit) - Predictable state management for React
- React-Router/React-Router-DOM - SPA Router for React + DOM binding
- React-Syntax-Highlighter - Code snippet syntax highlighting and formatting
- React-Testing-Library - DOM-based testing library
- Recharts - Charting library for React
- Redux Logger - Dev logging for Redux
- Redux-Saga - Manage application side-effects like fetching data
- TSS-React - Material UI deprecated JSS-based styling in favor of Emotion-based styling in MUIv5. TSS-React allows you to continue to use JSS styling syntax (e.g.,
makeStyles
,withStyles
) while being a small translation-layer over Emotion-based styling - TypeScript - Language extensions to ECMAScript that adds strong typings, allowing you to catch errors earlier in development
- Typeface Roboto - Typeface recommended for use with Material-UI
- Yup - Validate user input against a schema (recommended by authors of Formik for form field validation)
DOM-based tests are written in React-Testing-Library which is built on top of Jest. Non-DOM-based tests are straight Jest.
Testing References:
- How to Test React Components: the Complete Guide - Good intro to front-end testing
- Jest Expect Matchers - What application states you can test for
- User-event - Prefer user-event to "fire event" in your testing as a higher level of user-oriented abstraction
- DOM-Testing-Library Cheatsheet - Helps differentiate different DOM selection methods
- React-Testing-Library Common Mistakes - Avoid common RTL mistakes
- Debugging Tests - Howto debug tests in Chrome browser and Visual Studio Code
- Testing Examples:
Code coverage reports are generated when tests run. Coverage reports can be viewed in the coverage
directory, specifically coverage/lcov-report/index.html
.
- When testing form field entry, unless you are creating separate tests for entering different values, you may need to clear previously entered form field data before entering new data using userEvent.clear or the
{backspace}
option in userEvent.type - When firing userEvents in a form, you may need to
waitFor
an expected change to complete. Otherwise, you may see the warning:Warning: You seem to have overlapping act() calls
- Use the correct query. For example, if testing an element does not exist in the DOM, use
queryBy*
instead ofgetBy*
- Prefer more-specific queries (such as
*ByRole
,*ByTitle
,*byLabel
) to a general query (such as*ByText
) as this also tests element type and not just a string found in the DOM. Only use*ByTestId
as a last-resort if there are no other options - Use
within
if you want to find a node within another node see documentation
Often the most difficult aspect to creating UI-based automated tests is visualizing the actions taken during test to determine what's missing or incorrect.
To address this you can create a src/pages/TestPage.tsx
component and have it return the component you want to test with different attributes passed-in. Next, add this page to an app route in src/App.tsx
(i.e. <Route exact path="/test" component={TestPage} />
). You can then manually test using your local dev server at your new application test route https://localhost:3000/test.
- Analyzing app bundle size
- Measuring app performance
- Optimizing app performance
- Introducing the React profiler
This repository is released under the MIT license. View the local license file.