From 70984aba6abc028eb29d9874b43bcc857aee4abc Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Tue, 14 Nov 2023 15:14:42 -0500 Subject: [PATCH] update typescript configs, resolve typing errors, and configure package.json --- .eslintignore | 7 --- SECURITY.md | 16 +++--- api/app.ts | 6 +- api/constants.ts | 6 -- api/package.json | 5 +- api/tsconfig.json | 8 +-- client/src/components/InlineSVG.tsx | 6 +- client/src/components/LoginForm.tsx | 14 +++-- client/src/components/StoredRequestValue.tsx | 57 ++++++++----------- .../src/theme/NavbarItem/ComponentTypes.tsx | 5 +- client/tsconfig.json | 1 + custom-words.txt | 6 ++ package-lock.json | 5 +- package.json | 47 ++++++++++++--- 14 files changed, 109 insertions(+), 80 deletions(-) delete mode 100644 .eslintignore create mode 100644 custom-words.txt diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 5884efc..0000000 --- a/.eslintignore +++ /dev/null @@ -1,7 +0,0 @@ -**/build -**/dist -**/node_modules -**/webpack.*.js -**/.docusaurus - -.github/workflows diff --git a/SECURITY.md b/SECURITY.md index c1d5bf4..e6edb96 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,17 +2,17 @@ Bitwarden believes that working with security researchers across the globe is cr # Disclosure Policy -- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every effort to quickly resolve the issue. -- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a third-party. We may publicly disclose the issue before resolving it, if appropriate. -- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our service. Only interact with accounts you own or with explicit permission of the account holder. -- If you would like to encrypt your report, please use the PGP key with long ID `0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool). +- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every effort to quickly resolve the issue. +- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a third-party. We may publicly disclose the issue before resolving it, if appropriate. +- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our service. Only interact with accounts you own or with explicit permission of the account holder. +- If you would like to encrypt your report, please use the PGP key with long ID `0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool). While researching, we'd like to ask you to refrain from: -- Denial of service -- Spamming -- Social engineering (including phishing) of Bitwarden staff or contractors -- Any physical attempts against Bitwarden property or data centers +- Denial of service +- Spamming +- Social engineering (including phishing) of Bitwarden staff or contractors +- Any physical attempts against Bitwarden property or data centers # We want to help you! diff --git a/api/app.ts b/api/app.ts index 2d91acc..379b46c 100644 --- a/api/app.ts +++ b/api/app.ts @@ -2,7 +2,7 @@ import "dotenv/config"; const fs = require("fs"); const express = require("express"); import { Request, Response, NextFunction } from "express-serve-static-core"; -import { DEFAULT_COOKIE_SETTINGS, QUERY_PARAMS, ROUTES } from "./constants"; +import { QUERY_PARAMS, ROUTES } from "./constants"; const port = process.env.SERVE_PORT || 443; const insecurePort = process.env.SERVE_INSECURE_PORT || 80; @@ -50,7 +50,9 @@ function handleRequest(request: Request, response: Response, route: string) { response.cookie("referrerRequestBody", JSON.stringify(request.body), { path: responsePath, - ...DEFAULT_COOKIE_SETTINGS, + sameSite: "strict", + secure: true, + maxAge: 1000 * 60 * 5, }); try { diff --git a/api/constants.ts b/api/constants.ts index 5e07e27..46ea491 100644 --- a/api/constants.ts +++ b/api/constants.ts @@ -1,9 +1,3 @@ -export const DEFAULT_COOKIE_SETTINGS = { - sameSite: true, - secure: true, - maxAge: 1000 * 60 * 5, -}; - export const ROUTES = { IDENTITY: "/identity", LOGIN: "/login", diff --git a/api/package.json b/api/package.json index 17947b8..872fa57 100644 --- a/api/package.json +++ b/api/package.json @@ -5,9 +5,10 @@ "main": "app.ts", "scripts": { "build": "rimraf build && tsc", - "test": "echo \"Error: no test specified\" && exit 1", + "start:watch": "nodemon -x 'npm run start'", "start": "npm run build && node build/app.js", - "start:watch": "nodemon -x 'npm run start'" + "test": "echo \"Error: no test specified\" && exit 1", + "typecheck": "tsc" }, "dependencies": { "dotenv": "16.3.1", diff --git a/api/tsconfig.json b/api/tsconfig.json index 089f6b3..b57cad3 100644 --- a/api/tsconfig.json +++ b/api/tsconfig.json @@ -1,11 +1,11 @@ { "compilerOptions": { - "target": "es2016", - "module": "commonjs", - "outDir": "./build", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, + "module": "commonjs", + "outDir": "./build", + "skipLibCheck": true, "strict": true, - "skipLibCheck": true + "target": "es2016" } } diff --git a/client/src/components/InlineSVG.tsx b/client/src/components/InlineSVG.tsx index 5393a0f..82dc672 100644 --- a/client/src/components/InlineSVG.tsx +++ b/client/src/components/InlineSVG.tsx @@ -1,10 +1,10 @@ import Link from "@docusaurus/Link"; -type InlineSVGProps = { +export type InlineSVGProps = { href?: string; Svg: any; - width: string | number; - height: string | number; + width?: string | number; + height?: string | number; label: string; children?: JSX.Element; className?: string; diff --git a/client/src/components/LoginForm.tsx b/client/src/components/LoginForm.tsx index 8cd102a..f73dc23 100644 --- a/client/src/components/LoginForm.tsx +++ b/client/src/components/LoginForm.tsx @@ -8,6 +8,12 @@ const FormSteps = { type FormSteps = (typeof FormSteps)[keyof typeof FormSteps]; +type FormValues = { + username?: string; + email?: string; + password?: string; +}; + export function LoginForm({ action, isMultiStep = false, @@ -15,7 +21,7 @@ export function LoginForm({ action: string; isMultiStep?: boolean; }): JSX.Element { - const [formValues, setFormValues] = useState({}); + const [formValues, setFormValues] = useState>({}); const [currentFormStep, setCurrentFormStep] = useState>(); @@ -36,10 +42,10 @@ export function LoginForm({ } }, [formValues]); - function handleFormStep(event) { + function handleFormStep(event: React.FormEvent) { event.preventDefault(); - const formData = new FormData(event.target); + const formData = new FormData(event.currentTarget); setFormValues({ ...formValues, ...Object.fromEntries(formData as any) }); } @@ -126,7 +132,7 @@ function FormButton({ label }: { label: string }) { ); } -function submitFormData(action, data) { +function submitFormData(action: string, data: FormValues | {}) { fetch(action, { method: "POST", headers: { diff --git a/client/src/components/StoredRequestValue.tsx b/client/src/components/StoredRequestValue.tsx index 154e98c..45c05cc 100644 --- a/client/src/components/StoredRequestValue.tsx +++ b/client/src/components/StoredRequestValue.tsx @@ -1,7 +1,7 @@ -import BrowserOnly from "@docusaurus/BrowserOnly"; import CodeBlock from "@theme/CodeBlock"; +import useIsBrowser from "@docusaurus/useIsBrowser"; -function formatStringifiedValue(stringifiedJSON) { +function formatStringifiedValue(stringifiedJSON: string) { const decodedString = decodeURIComponent(stringifiedJSON); // parse and re-stringify to use stringify's built-in formatting @@ -9,36 +9,29 @@ function formatStringifiedValue(stringifiedJSON) { } export function StoredRequestValue() { + const isBrowser = useIsBrowser(); + let requestBodyValue; + + if (isBrowser) { + const cookieKey = "referrerRequestBody"; + const cookieValue = document.cookie + .split("; ") + .find((row) => row.startsWith(`${cookieKey}=`)) + ?.split("=")[1]; + + if (!cookieValue) { + return null; + } + + requestBodyValue = formatStringifiedValue(cookieValue); + + // clear cookie value + document.cookie = `${cookieKey}=; SameSite=Strict; Max-Age=0`; + } + return ( - - {() => { - const cookieKey = "referrerRequestBody"; - const cookieValue = document.cookie - .split("; ") - .find((row) => row.startsWith(`${cookieKey}=`)) - ?.split("=")[1]; - - if (!cookieValue) { - return null; - } - - const requestBodyValue = formatStringifiedValue(cookieValue); - - // clear cookie value - document.cookie = `${cookieKey}=; Max-Age=0`; - - return ( - <> - - {requestBodyValue} - - - ); - }} - + + {requestBodyValue} + ); } diff --git a/client/src/theme/NavbarItem/ComponentTypes.tsx b/client/src/theme/NavbarItem/ComponentTypes.tsx index e89f59e..b1a0850 100644 --- a/client/src/theme/NavbarItem/ComponentTypes.tsx +++ b/client/src/theme/NavbarItem/ComponentTypes.tsx @@ -1,9 +1,10 @@ import ComponentTypes from "@theme-original/NavbarItem/ComponentTypes"; -import { InlineSVG } from "@site/src/components/InlineSVG"; +import { InlineSVG, InlineSVGProps } from "@site/src/components/InlineSVG"; import GithubLogo from "@site/static/img/icons/github.svg"; import AngleRight from "@site/static/img/icons/angle-right.svg"; +import { Props as NavbarItemType } from "@theme/NavbarItem"; -function GithubIcon(props) { +function GithubIcon(props: NavbarItemType & InlineSVGProps): JSX.Element { return ; } diff --git a/client/tsconfig.json b/client/tsconfig.json index f7ef48e..5bcaaf1 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -3,6 +3,7 @@ "extends": "@docusaurus/tsconfig", "compilerOptions": { "baseUrl": ".", + "esModuleInterop": true, "jsx": "react-jsx", "strict": true } diff --git a/custom-words.txt b/custom-words.txt new file mode 100644 index 0000000..484a15e --- /dev/null +++ b/custom-words.txt @@ -0,0 +1,6 @@ +Bitwarden +dotfile +jsmith +keyserver +sandboxed +TOTP diff --git a/package-lock.json b/package-lock.json index 526063e..2a2b7b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,14 @@ { - "name": "test-the-web", + "name": "@bitwarden/test-the-web", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "test-the-web", + "name": "@bitwarden/test-the-web", "version": "0.0.0", "hasInstallScript": true, + "license": "SEE LICENSE IN LICENSE.txt", "devDependencies": { "cspell": "8.0.0", "husky": "8.0.3", diff --git a/package.json b/package.json index a86b79b..18c5fd7 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,31 @@ { - "name": "test-the-web", + "name": "@bitwarden/test-the-web", "version": "0.0.0", + "repository": { + "type": "git", + "url": "git+https://github.com/bitwarden/test-the-web.git" + }, + "author": "Bitwarden Inc. (https://bitwarden.com)", + "license": "SEE LICENSE IN LICENSE.txt", + "bugs": { + "url": "https://github.com/bitwarden/test-the-web/issues" + }, "private": true, "scripts": { - "prepare": "husky install", - "postinstall": "(npm run ci:api) && (npm run ci:client)", + "build:api": "cd api && npm run build", + "build:client": "cd client && npm run build", + "build:watch": "(cd client && npm run build:watch) & (cd api && npm run start:watch)", + "build": "(npm run build:api) && (npm run build:client)", "ci:api": "cd api && npm ci", "ci:client": "cd client && npm ci", - "build": "(npm run build:api) && (npm run build:client)", - "build:watch": "(cd client && npm run build:watch) & (cd api && npm run start:watch)", - "build:api": "cd api && npm run build", - "build:client": "cd client && npm run build" + "lint": "prettier --check .", + "postinstall": "(npm run ci:api) && (npm run ci:client)", + "prepare": "husky install", + "prettier": "prettier --write .", + "spellcheck": "cspell lint \"**/*.md{x,}\"", + "typecheck:api": "cd api && npm run typecheck", + "typecheck:client": "cd client && npm run typecheck", + "typecheck": "(npm run typecheck:api) && (npm run typecheck:client)" }, "devDependencies": { "cspell": "8.0.0", @@ -22,7 +37,23 @@ "typescript": "5.2.2" }, "lint-staged": { - "*": "prettier --cache --write --ignore-unknown" + "*": "prettier --cache --write --ignore-unknown", + "*.md{x,}": "cspell lint" + }, + "cspell": { + "version": "0.2", + "useGitignore": true, + "dictionaries": [ + "custom-words" + ], + "dictionaryDefinitions": [ + { + "name": "custom-words", + "path": "./custom-words.txt", + "addWords": true + } + ], + "languageId": "typescript,javascript,html,css,markdown,mdx" }, "browserslist": { "production": [