Skip to content

Commit

Permalink
Merge pull request #11 from WorkAroundHQ/fix/differentStuff
Browse files Browse the repository at this point in the history
Fix various stuff
  • Loading branch information
Maurice authored Nov 15, 2021
2 parents 5cfabab + 8b2fd1a commit 1d7589d
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 153 deletions.
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ cd app
yarn
```

3. Add values to `.env` file (see `sample.env`):
3. Add values to `.env` file (see [`sample.env`](https://github.com/WorkAroundHQ/app/blob/main/sample.env))

4. Start development server:

Expand All @@ -46,14 +46,22 @@ yarn test

Run cypress tests:

1. Add values to `cypress.env.json` (see `sample.cypress.env.json`)
1. Add values to `cypress.env.json` (see [`sample.cypress.env.json`](https://github.com/WorkAroundHQ/app/blob/main/sample.cypress.env.json))

2. Start cypress:
2. Start development server:

```zsh
yarn start
```

3. Start cypress:

```zsh
yarn cy
```

4. Start test in Cypress app.

## Deployment

The deployment process is simple:
Expand Down Expand Up @@ -82,6 +90,10 @@ WorkAround ist hosted on [`Netlify's`](https://www.netlify.com) CDN Network to p

These services can be accessed via different REST endpoints, dynamically created by Supabase.

## Security Measures

Have a look on our [Security](https://github.com/WorkAroundHQ/app/blob/main/SECURITY.md) Measures.

## Feedback

If you have any feedback, please reach out to us [@WorkAroundHQ](https://twitter.com/workaroundhq)
Expand Down
123 changes: 123 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Security

In the development process of WorkAround, we try to implement a as secure system as possible.

## Architecture Overview

![WorkAround-Threat-Model](https://user-images.githubusercontent.com/28442090/141773605-18deb518-b01a-42eb-88b0-ee09fb4ff0ed.jpg)

## Threat Model

### Weak Credentials

_Users can create weak passwords for their authentication._

Protection:

- Users can't create passwords shorter than 12 characters.

Action:

- Inform users to use a strong password when they try use a weak one.

### No Session Timeout

_The session created after a user login has no reasonable timeout duration._

Protection:

- Sessions timeout after 1 hour.

### SQL Injection

_An attacker could inject malicious SQL statements to retrieve sensitive data._

Protection:

- Use of client library to not directly write SQL statements (ORM-like behavior).

Detection:

- Regular checks of logs

Action:

- Revert malicious changes

- Inform users if sensitive data got stolen or publicly available.

### Dependency Vulnerablities

_Dependencies used by the application are vulnerable for misuse by an attacker._

Protection:

- Use of only the most recent/secure versions possible (Sometimes not possible due to peer dependencies).

Detection:

- GitHub Dependabot alerts when a dependency has known security vulnerabilities.

Action:

- Update vulnerable dependencies to secure version.

### Poor handling of secrets

_Secrets are stored in plain text in VCS or can be read by malicious processes, like CI/CD._

Protection:

- Secrets aren't stored in the VCS.

- CI/CD Pipelines can just access repository secrets through an authorized user.

- CI/CD Pipelines can't expose secrets through GitHubs policies.

- Forks don't have access to repository secrets.

### Verbose error messages

_Error messages give too much information about the business logic of an application which can be used by an attacker._

Protection:

- Error messages shown to the user do not expose business logic which could help to attack the system.

- Auth related error message don't expose if the email or the password was wrong at a login try.

### Broken Access Control

_User can access data they should have no access to._

Protection:

- Through the use of Postgres Policies users can't access data they should not access.

Detection:

- `OPEN`: Log database actions

Action:

- Inform users if sensitive data got stolen or publicly available.

### Fails to prevent clickjacking

_An attacker could implement an iframe with the application on another website to hijack users credentials or to perform actions in their names._

Protection:

- Through the Content-Security-Policy directive `frame-ancestors 'none';` the application can't be embedded in another website.

### CSP configuration

_An attacker can inject malicious code through a loosely configured CSP._

Protection:

- `WIP`: A deny-by-default policy restricts executable sources to a minimum.

## Resources

- STRIDE Framework: https://martinfowler.com/articles/agile-threat-modelling.html#UseStrideToHelp
42 changes: 21 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,29 @@
"private": true,
"homepage": "https://app.workaround.world",
"devDependencies": {
"@testing-library/cypress": "8.0.1",
"@testing-library/dom": "8.11.1",
"@testing-library/jest-dom": "5.11.4",
"@testing-library/react": "12.1.2",
"@testing-library/user-event": "13.5.0",
"cross-env": "7.0.3",
"cypress": "8.0.0",
"msw": "0.35.0",
"node-sass": "6.0.1",
"sass-loader": "12.1.0",
"web-vitals": "1.0.1"
"@testing-library/cypress": "^8.0.1",
"@testing-library/dom": "^8.11.1",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
"cross-env": "^7.0.3",
"cypress": "^8.0.0",
"msw": "^0.35.0",
"node-sass": "^6.0.1",
"sass-loader": "^12.1.0",
"web-vitals": "^1.0.1"
},
"dependencies": {
"@supabase/supabase-js": "1.25.0",
"ansi-regex": "5.0.1",
"browserslist": "4.16.5",
"glob-parent": "5.1.2",
"nth-check": "2.0.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-router-dom": "5.3.0",
"react-scripts": "4.0.3",
"set-value": "4.0.1"
"@supabase/supabase-js": "^1.25.0",
"ansi-regex": "^5.0.1",
"browserslist": "^4.16.5",
"glob-parent": "^5.1.2",
"nth-check": "^2.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.3.0",
"react-scripts": "^4.0.3",
"set-value": "^4.0.1"
},
"scripts": {
"start": "react-scripts start",
Expand Down
2 changes: 2 additions & 0 deletions sample.cypress.env.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Test credentials for the backend to run complete end-to-end tests.
// Remove these comments in the final cypress.env.json file.
{
"CYPRESS_TEST_EMAIL": "",
"CYPRESS_TEST_PW": ""
Expand Down
8 changes: 8 additions & 0 deletions sample.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
# To retrieve these variables:
# 1. Go to supabase.io
# 2. Select WorkAround Project
# 3. Go to Settings
# 4. In Settings, go to API
# 5. Under Project API Keys, copy anon public
# 6. Under Config, copy URL
# Remove these comments for the final .env file.
SUPABASE_ANON_KEY=
SUPABASE_URL=
70 changes: 42 additions & 28 deletions src/pages/Auth.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,33 @@ const Auth = () => {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [repeatedPassword, setRepeatedPassword] = useState('')
const [errorMessage, setErrorMessage] = useState('')

const handleLogin = async (email, password) => {
try {
setLoading(true)
const { error } = await supabase.auth.signIn({
email: email,
password: password
})
if (error) throw error
} catch (error) {
alert(error.error_description || error.message)
} finally {
setLoading(false)
if (email.length !== 0) {
if (password.length !== 0) {
try {
setLoading(true)
const { error } = await supabase.auth.signIn({
email: email,
password: password
})
if (error) throw error
} catch (error) {
setErrorMessage(error.error_description || error.message)
} finally {
setLoading(false)
}
} else {
setErrorMessage('Please enter your password')
}
} else {
setErrorMessage('Please enter your email')
}
}

const handleSignup = async (email, password, repeatedPassword) => {
if (password.length > 8) {
if (password.length >= 12) {
if (password === repeatedPassword) {
try {
setLoading(true)
Expand All @@ -39,25 +48,25 @@ const Auth = () => {
if (error) throw error
alert('Check your email for the confirmation link!')
} catch (error) {
alert(error.error_description || error.message)
setErrorMessage(error.error_description || error.message)
} finally {
setLoading(false)
}
} else {
alert('The passwords are not equal. Please try again')
setErrorMessage('The passwords are not equal. Please try again')
}
} else {
alert('The password is not long enough. At least 8 characters are required')
setErrorMessage('The password is not long enough. At least 12 characters are required')
}
}

const handlePrimaryButtonClick = async (e) => {
e.preventDefault()
if (signUpMode) {
handleSignup(email, password, repeatedPassword)
} else {
handleLogin(email, password)
}
e.preventDefault()
if (signUpMode) {
handleSignup(email, password, repeatedPassword)
} else {
handleLogin(email, password)
}
}

const handleEnterPress = (e) => {
Expand All @@ -74,20 +83,25 @@ const Auth = () => {
</div>
<div className="auth-form">
<div className='auth-inputs'>
<Input type='email' placeholder='Your email' onChange={(e) => setEmail(e.target.value)} onEnter={(e) => handleEnterPress(e)} />
<Input type='password' placeholder='Password' onChange={(e) => setPassword(e.target.value)} onEnter={(e) => handleEnterPress(e)} />
{signUpMode ?
<Input type='password' placeholder='Repeat Password' onChange={(e) => setRepeatedPassword(e.target.value)} onEnter={(e) => handleEnterPress(e)} /> : ''
}
</div>
<Button text={loading ? 'Loading' : signUpMode ? 'Sign Up' : 'Login'} mode='primary' disabled={loading} onClick={e => handlePrimaryButtonClick(e)} />
<Input type='email' placeholder='Your email' onChange={(e) => setEmail(e.target.value)} onEnter={(e) => handleEnterPress(e)} />
<Input type='password' placeholder='Password' onChange={(e) => setPassword(e.target.value)} onEnter={(e) => handleEnterPress(e)} />
{signUpMode ?
<Input type='password' placeholder='Repeat Password' onChange={(e) => setRepeatedPassword(e.target.value)} onEnter={(e) => handleEnterPress(e)} /> : ''
}
</div>
<Button text={loading ? 'Loading' : signUpMode ? 'Sign Up' : 'Login'} mode='primary' disabled={loading} onClick={e => handlePrimaryButtonClick(e)} />
<div className={`auth-error${errorMessage === '' ? ' hidden' : ' visible'}`}>
<ion-icon name="alert-circle"></ion-icon>
<p className='auth-error-text'>{errorMessage}</p>
</div>
</div>
</div>
<div className="auth-footer">
<p>{signUpMode ? 'Already have an account? Login ' : 'Don\'t have an account? Sign up '}
<button onClick={(e) => {
e.preventDefault()
setAuthMode(!signUpMode)
setErrorMessage('')
}}>here</button>
</p>
</div>
Expand Down
Loading

0 comments on commit 1d7589d

Please sign in to comment.