mirror of
https://github.com/foomo/foomo-docs.git
synced 2025-10-16 12:35:40 +00:00
Compare commits
16 Commits
28842aaa0f
...
e5898ab222
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5898ab222 | ||
|
|
516550167e | ||
|
|
9f2425e847 | ||
|
|
b66b2d30ca | ||
|
|
e17d1f23b6 | ||
|
|
e8e443f1e0 | ||
|
|
5821bbfe8a | ||
|
|
e90b31ee0d | ||
|
|
4680527d5a | ||
|
|
c49f43d17b | ||
|
|
20eb4fe5c0 | ||
|
|
876de8da7a | ||
|
|
58aa255cc0 | ||
|
|
1f6448e1a0 | ||
|
|
109fca03fc | ||
|
|
c87f1a57a3 |
4
.github/workflows/publish.yml
vendored
4
.github/workflows/publish.yml
vendored
@ -17,7 +17,9 @@ jobs:
|
|||||||
- uses: actions/setup-node@v5
|
- uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: "20.x"
|
node-version: "20.x"
|
||||||
cache: yarn
|
|
||||||
|
- name: Enable Corepack
|
||||||
|
run: corepack enable
|
||||||
|
|
||||||
- name: Release to GitHub Pages
|
- name: Release to GitHub Pages
|
||||||
working-directory: foomo
|
working-directory: foomo
|
||||||
|
|||||||
4
.github/workflows/validate.yml
vendored
4
.github/workflows/validate.yml
vendored
@ -17,7 +17,9 @@ jobs:
|
|||||||
- uses: actions/setup-node@v5
|
- uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: "20.x"
|
node-version: "20.x"
|
||||||
cache: yarn
|
|
||||||
|
- name: Enable Corepack
|
||||||
|
run: corepack enable
|
||||||
|
|
||||||
- name: Test Build
|
- name: Test Build
|
||||||
working-directory: foomo
|
working-directory: foomo
|
||||||
|
|||||||
2
foomo/.gitignore
vendored
2
foomo/.gitignore
vendored
@ -20,3 +20,5 @@ npm-debug.log*
|
|||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
|
|
||||||
|
.yarn
|
||||||
1
foomo/.yarnrc.yml
Normal file
1
foomo/.yarnrc.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
nodeLinker: node-modules
|
||||||
@ -4,13 +4,13 @@ This website is built using [Docusaurus 2](https://docusaurus.io/), a modern sta
|
|||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
```
|
```shell
|
||||||
$ yarn
|
$ yarn
|
||||||
```
|
```
|
||||||
|
|
||||||
### Local Development
|
### Local Development
|
||||||
|
|
||||||
```
|
```shell
|
||||||
$ yarn start
|
$ yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ This command starts a local development server and opens up a browser window. Mo
|
|||||||
|
|
||||||
### Build
|
### Build
|
||||||
|
|
||||||
```
|
```shell
|
||||||
$ yarn build
|
$ yarn build
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ This command generates static content into the `build` directory and can be serv
|
|||||||
|
|
||||||
### Deployment
|
### Deployment
|
||||||
|
|
||||||
```
|
```shell
|
||||||
$ GIT_USER=<Your GitHub username> USE_SSH=true yarn deploy
|
$ GIT_USER=<Your GitHub username> USE_SSH=true yarn deploy
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -77,7 +77,7 @@ The summary shows we are only covering 22% of the code. The goal is not to cover
|
|||||||
|
|
||||||
The question is: how do we know exactly which lines of code we're covering through the test suite? Again, go test comes to the rescue through another option: _-coverprofile_ lets us specify an output file that will contain references to each single line of code involved in the analysis. It is a text file, but not very readable:
|
The question is: how do we know exactly which lines of code we're covering through the test suite? Again, go test comes to the rescue through another option: _-coverprofile_ lets us specify an output file that will contain references to each single line of code involved in the analysis. It is a text file, but not very readable:
|
||||||
|
|
||||||
```
|
```go
|
||||||
github.com/foom...tapi/gocontentfulvolibproduct.go:21.86,22.15 1 1
|
github.com/foom...tapi/gocontentfulvolibproduct.go:21.86,22.15 1 1
|
||||||
github.com/foom...tapi/gocontentfulvolibproduct.go:25.2,25.18 1 1
|
github.com/foom...tapi/gocontentfulvolibproduct.go:25.2,25.18 1 1
|
||||||
github.com/foom...tapi/gocontentfulvolibproduct.go:28.2,29.16 2 0
|
github.com/foom...tapi/gocontentfulvolibproduct.go:28.2,29.16 2 0
|
||||||
|
|||||||
@ -3,7 +3,7 @@ Title: CLI applications
|
|||||||
position: 12
|
position: 12
|
||||||
---
|
---
|
||||||
|
|
||||||
import { GoPlayground } from '../../../src/components/GoPlayground';
|
import { GoPlayground } from '@site/src/components/GoPlayground';
|
||||||
|
|
||||||
# CLI applications
|
# CLI applications
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,9 @@ When creating resources the most important thing is to be consistent.
|
|||||||
|
|
||||||
The following resource convention is preferred if no other exist in the project:
|
The following resource convention is preferred if no other exist in the project:
|
||||||
|
|
||||||
```[customer*]-[project]-[resource*]-[env*]-[name*]-[region/location]-[index]```
|
```text
|
||||||
|
[customer*]-[project]-[resource*]-[env*]-[name*]-[region/location]-[index]
|
||||||
|
```
|
||||||
|
|
||||||
The fields with a * are required.
|
The fields with a * are required.
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
sidebar_label: "Objects"
|
sidebar_label: "Objects"
|
||||||
---
|
---
|
||||||
import { TypeScriptPlayground } from "../../../src/components/TypeScriptPlayground";
|
import { TypeScriptPlayground } from "@site/src/components/TypeScriptPlayground";
|
||||||
|
|
||||||
# Object declaration syntax in TypeScript
|
# Object declaration syntax in TypeScript
|
||||||
|
|
||||||
|
|||||||
@ -1,23 +1,14 @@
|
|||||||
---
|
---
|
||||||
id: index
|
sidebar_position: 4
|
||||||
sidebar_position: 0
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# General guide
|
# General
|
||||||
|
|
||||||
This is a general guide for software developers.
|
- [Essentials](./essentials.md)
|
||||||
|
- [Package Managers](./package-managers.md)
|
||||||
If you are looking for docs of the foomo project - start [here](projects)
|
- [Security](/docs/security/)
|
||||||
|
- [Setup](./setup/computer.md)
|
||||||
## Get started
|
- [Technologies](./technologies/SSE.md)
|
||||||
|
- [Utilities](./utilities/k9s.md)
|
||||||
- [setup your workplace](general/setup/workplace)
|
- [Work](./work/general.md)
|
||||||
- [security is always first](general/security)
|
|
||||||
- [setup your computer](general/setup/computer)
|
|
||||||
- [look at the essentials](general/essentials)
|
|
||||||
- [make sure you are using a package manager](general/package-managers)
|
|
||||||
- [install some awesome software](/awesome-software)
|
|
||||||
- [more for frontend devs](frontend)
|
|
||||||
- [more for backend devs](backend)
|
|
||||||
- [start exploring the foomo projects](projects)
|
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,7 @@ This is a general setup guide for your computer. Make sure that you setup your [
|
|||||||
|
|
||||||
## Privacy and security
|
## Privacy and security
|
||||||
|
|
||||||
Follow the instructions on [security](../security) when you setup and maintain your system.
|
Follow the instructions on [security](../../security) when you setup and maintain your system.
|
||||||
|
|
||||||
## Install software
|
## Install software
|
||||||
|
|
||||||
|
|||||||
@ -40,4 +40,4 @@ module:
|
|||||||
name: github.com/foomo/gotsrpc
|
name: github.com/foomo/gotsrpc
|
||||||
path: ../ # Relative Or Absolute Path where the package was checked out (root of the package)
|
path: ../ # Relative Or Absolute Path where the package was checked out (root of the package)
|
||||||
|
|
||||||
fe```
|
```
|
||||||
|
|||||||
@ -52,7 +52,7 @@ When the `IsSuccessful`-function returns an error (and the ignore value is set t
|
|||||||
### Examples
|
### Examples
|
||||||
Let's say we want to stop sending requests once we encountered three consecutive failures.
|
Let's say we want to stop sending requests once we encountered three consecutive failures.
|
||||||
|
|
||||||
``` go
|
```go
|
||||||
client := keelhttp.NewHTTPClient(
|
client := keelhttp.NewHTTPClient(
|
||||||
keelhttp.HTTPClientWithRoundTripware(l,
|
keelhttp.HTTPClientWithRoundTripware(l,
|
||||||
roundtripware.CircuitBreaker(&roundtripware.CircuitBreakerSettings{
|
roundtripware.CircuitBreaker(&roundtripware.CircuitBreakerSettings{
|
||||||
@ -75,7 +75,7 @@ client := keelhttp.NewHTTPClient(
|
|||||||
|
|
||||||
Now lets say we see we also want to detect network problems such as a BadGateway. For this we can use the `IsSuccessful` option.
|
Now lets say we see we also want to detect network problems such as a BadGateway. For this we can use the `IsSuccessful` option.
|
||||||
|
|
||||||
``` go
|
```go
|
||||||
client := keelhttp.NewHTTPClient(
|
client := keelhttp.NewHTTPClient(
|
||||||
keelhttp.HTTPClientWithRoundTripware(l,
|
keelhttp.HTTPClientWithRoundTripware(l,
|
||||||
roundtripware.CircuitBreaker(&roundtripware.CircuitBreakerSettings{
|
roundtripware.CircuitBreaker(&roundtripware.CircuitBreakerSettings{
|
||||||
@ -99,7 +99,7 @@ client := keelhttp.NewHTTPClient(
|
|||||||
|
|
||||||
Lastly, let's assume we use the client for multiple different endpoints. And we only want to base the circuit breakers state on a single endpoint, but stop request on all endpoints once the breaker changes to open. Again we can use the IsSuccessful option and ignore certain endpoints.
|
Lastly, let's assume we use the client for multiple different endpoints. And we only want to base the circuit breakers state on a single endpoint, but stop request on all endpoints once the breaker changes to open. Again we can use the IsSuccessful option and ignore certain endpoints.
|
||||||
|
|
||||||
``` go
|
```go
|
||||||
client := keelhttp.NewHTTPClient(
|
client := keelhttp.NewHTTPClient(
|
||||||
keelhttp.HTTPClientWithRoundTripware(l,
|
keelhttp.HTTPClientWithRoundTripware(l,
|
||||||
roundtripware.CircuitBreaker(&roundtripware.CircuitBreakerSettings{
|
roundtripware.CircuitBreaker(&roundtripware.CircuitBreakerSettings{
|
||||||
@ -125,7 +125,7 @@ client := keelhttp.NewHTTPClient(
|
|||||||
#### Using ratios in ReadyToTrip
|
#### Using ratios in ReadyToTrip
|
||||||
When using ratios in ready to trip, the `Interval` should be set to a non-zero value in order to reset the counts periodically. Otherwise, after a long period of successful requests it will also take a long time to impact the ratio and trip the breaker.
|
When using ratios in ready to trip, the `Interval` should be set to a non-zero value in order to reset the counts periodically. Otherwise, after a long period of successful requests it will also take a long time to impact the ratio and trip the breaker.
|
||||||
|
|
||||||
``` go
|
```go
|
||||||
ReadyToTrip: func(counts gobreaker.Counts) bool {
|
ReadyToTrip: func(counts gobreaker.Counts) bool {
|
||||||
failureRatio := float64(counts.TotalFailures) / float64(counts.Requests)
|
failureRatio := float64(counts.TotalFailures) / float64(counts.Requests)
|
||||||
return counts.Requests >= 3 && failureRatio >= 0.6
|
return counts.Requests >= 3 && failureRatio >= 0.6
|
||||||
|
|||||||
580
foomo/docs/security/go.md
Normal file
580
foomo/docs/security/go.md
Normal file
@ -0,0 +1,580 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
# Go Security Best Practices
|
||||||
|
|
||||||
|
Go is a modern programming language that has gained popularity for its simplicity, efficiency, and strong support for concurrent programming. However, like any other language, writing secure Go code requires an understanding of common vulnerabilities and best practices to mitigate them. This guide provides a comprehensive overview of Go security, drawing from official documentation and community-driven projects.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [Vulnerability Management](#vulnerability-management)
|
||||||
|
- [Keeping Go and Dependencies Updated](#keeping-go-and-dependencies-updated)
|
||||||
|
- [Vulnerability Scanning with `govulncheck`](#vulnerability-scanning-with-govulncheck)
|
||||||
|
- [Secure Coding Practices](#secure-coding-practices)
|
||||||
|
- [Input Validation](#input-validation)
|
||||||
|
- [SQL Injection](#sql-injection)
|
||||||
|
- [Cross-Site Scripting (XSS)](#cross-site-scripting-xss)
|
||||||
|
- [Directory Traversal](#directory-traversal)
|
||||||
|
- [Command Injection](#command-injection)
|
||||||
|
- [Static Analysis Security Testing (SAST)](#static-analysis-security-testing-sast)
|
||||||
|
- [`gosec`](#gosec)
|
||||||
|
- [Go's Built-in Security Features](#gos-built-in-security-features)
|
||||||
|
- [Race Detector](#race-detector)
|
||||||
|
- [Fuzz Testing](#fuzz-testing)
|
||||||
|
- [Go Vet](#go-vet)
|
||||||
|
- [Common Go Mistakes](#common-go-mistakes)
|
||||||
|
- [Security Checklist](#security-checklist)
|
||||||
|
- [Further Reading and References](#further-reading-and-references)
|
||||||
|
|
||||||
|
|
||||||
|
## Vulnerability Management
|
||||||
|
|
||||||
|
A key aspect of maintaining a secure application is managing dependencies and scanning for known vulnerabilities.
|
||||||
|
|
||||||
|
### Keeping Go and Dependencies Updated
|
||||||
|
|
||||||
|
The Go team regularly releases new versions of the language that include security patches, bug fixes, and performance improvements. It is crucial to keep your Go version up-to-date. You can check for the latest release on the [official Go website](https://go.dev/dl/).
|
||||||
|
|
||||||
|
Similarly, third-party dependencies can introduce vulnerabilities. Use `go list -m -json all` to review your project's dependencies and ensure they are current. To understand why a specific package is a dependency, you can use `go mod why -m <module-path>`.
|
||||||
|
|
||||||
|
### Vulnerability Scanning with `govulncheck`
|
||||||
|
|
||||||
|
The Go team provides `govulncheck`, a tool to scan your project for known vulnerabilities. It's backed by the [Go vulnerability database](https://vuln.go.dev). What makes `govulncheck` particularly effective is that it only reports vulnerabilities in functions that your code is actually calling, reducing noise from irrelevant alerts.
|
||||||
|
|
||||||
|
To scan your project, run:
|
||||||
|
```bash
|
||||||
|
go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||||
|
govulncheck ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
`govulncheck` can be integrated into your CI/CD pipeline to automate vulnerability detection. The Go team provides a [GitHub Action for `govulncheck`](https://github.com/marketplace/actions/run-govulncheck).
|
||||||
|
|
||||||
|
## Secure Coding Practices
|
||||||
|
|
||||||
|
The [OWASP Go Secure Coding Practices guide](https://github.com/OWASP/Go-SCP) is an excellent resource for developers. It covers a wide range of security topics.
|
||||||
|
|
||||||
|
### Input Validation
|
||||||
|
|
||||||
|
Always validate and sanitize input from external sources, such as users, APIs, or files. This is the first line of defense against many types of attacks, including injection attacks.
|
||||||
|
|
||||||
|
**Example: Validating a numeric ID**
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
idStr := r.URL.Query().Get("id")
|
||||||
|
id, err := strconv.Atoi(idStr)
|
||||||
|
if err != nil || id <= 0 {
|
||||||
|
http.Error(w, "Invalid user ID", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// ... proceed with fetching user
|
||||||
|
fmt.Fprintf(w, "Fetching user with ID: %d", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/user", getUserHandler)
|
||||||
|
http.ListenAndServe(":8080", nil)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### SQL Injection
|
||||||
|
|
||||||
|
Use prepared statements with placeholders for parameters to prevent SQL injection. Avoid constructing SQL queries by concatenating strings. The `database/sql` package natively supports parameterized queries.
|
||||||
|
|
||||||
|
**Example: Secure Database Query**
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"log"
|
||||||
|
// Assuming a PostgreSQL driver
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID int
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUserByUsername(db *sql.DB, username string) (*User, error) {
|
||||||
|
// This query is safe from SQL injection because the username is passed
|
||||||
|
// as a parameter, not concatenated into the query string.
|
||||||
|
query := "SELECT id, name FROM users WHERE username = $1"
|
||||||
|
|
||||||
|
row := db.QueryRow(query, username)
|
||||||
|
|
||||||
|
var user User
|
||||||
|
if err := row.Scan(&user.ID, &user.Name); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil // User not found
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Example usage
|
||||||
|
// db, err := sql.Open("postgres", "your-connection-string")
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal(err)
|
||||||
|
// }
|
||||||
|
// defer db.Close()
|
||||||
|
//
|
||||||
|
// user, err := GetUserByUsername(db, "some_user")
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cross-Site Scripting (XSS)
|
||||||
|
|
||||||
|
Go's `html/template` package provides automatic contextual escaping, which is a powerful defense against XSS. It understands the context in which data is being inserted (HTML body, attribute, URL) and applies the appropriate escaping.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func searchHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
query := r.URL.Query().Get("q")
|
||||||
|
// The template package automatically escapes the query variable.
|
||||||
|
tmpl, _ := template.New("search").Parse(`<h1>Search Results</h1><p>You searched for: {{.}}</p>`)
|
||||||
|
tmpl.Execute(w, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/search", searchHandler)
|
||||||
|
http.ListenAndServe(":8080", nil)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
If you run this example and provide a query like `<script>alert('XSS')</script>`, the output will be safely escaped to `<script>alert('XSS')</script>`.
|
||||||
|
|
||||||
|
### Directory Traversal
|
||||||
|
|
||||||
|
A directory traversal (or path traversal) attack allows an attacker to access files and directories outside the web root directory. Sanitize file paths from user input to prevent this.
|
||||||
|
|
||||||
|
**Example: Safe File Serving**
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func serveFileHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Get the filename from the query parameter
|
||||||
|
filename := r.URL.Query().Get("file")
|
||||||
|
if filename == "" {
|
||||||
|
http.Error(w, "Filename not specified", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean the path to prevent directory traversal.
|
||||||
|
// This resolves '..' and other path manipulations.
|
||||||
|
// For example, "../../secret.txt" becomes "secret.txt".
|
||||||
|
cleanedPath := filepath.Clean(filename)
|
||||||
|
|
||||||
|
// Construct the full path within a safe directory
|
||||||
|
safeDir := "/var/www/data"
|
||||||
|
fullPath := filepath.Join(safeDir, cleanedPath)
|
||||||
|
|
||||||
|
// Serve the file
|
||||||
|
http.ServeFile(w, r, fullPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/static", serveFileHandler)
|
||||||
|
http.ListenAndServe(":8080", nil)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Command Injection
|
||||||
|
|
||||||
|
Command injection vulnerabilities occur when an application passes unsanitized user input to a system shell. Use the `os/exec` package to invoke system commands, which avoids shell interpretation of arguments.
|
||||||
|
|
||||||
|
**Example: Safe Command Execution**
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Unsafe - vulnerable to command injection if `userInput` is `"; rm -rf /"`
|
||||||
|
// cmd := exec.Command("bash", "-c", "echo " + userInput)
|
||||||
|
|
||||||
|
// Safe - the arguments are passed directly to the command, not interpreted by a shell.
|
||||||
|
userInput := "some argument"
|
||||||
|
cmd := exec.Command("echo", userInput)
|
||||||
|
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Command failed: %v\nOutput: %s", err, output)
|
||||||
|
}
|
||||||
|
log.Printf("Output: %s", output)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Static Analysis Security Testing (SAST)
|
||||||
|
|
||||||
|
Static analysis tools can help identify security flaws in your code before it runs.
|
||||||
|
|
||||||
|
### `gosec`
|
||||||
|
|
||||||
|
[`gosec`](https://github.com/securego/gosec) is a popular Go security checker that scans Go source code for potential security issues.
|
||||||
|
|
||||||
|
To install and run `gosec`:
|
||||||
|
```bash
|
||||||
|
go install github.com/securego/gosec/v2/cmd/gosec@latest
|
||||||
|
gosec ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
`gosec` can detect a wide range of issues, including:
|
||||||
|
* `G101`: Hardcoded credentials
|
||||||
|
* `G402`: Look for TLS misconfigurations
|
||||||
|
* `G404`: Insecure use of random numbers
|
||||||
|
* `G204`: Subprocess launched with variable
|
||||||
|
* `G304`: File path provided as taint input
|
||||||
|
|
||||||
|
**Example of an issue `gosec` would flag:**
|
||||||
|
```go
|
||||||
|
// gosec will warn about using math/rand for security-sensitive operations.
|
||||||
|
// Use crypto/rand instead.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func generateToken() string {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
// G404: Use of weak random number generator (math/rand instead of crypto/rand)
|
||||||
|
b := make([]byte, 16)
|
||||||
|
rand.Read(b)
|
||||||
|
return fmt.Sprintf("%x", b)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**Correction using `crypto/rand`:**
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func generateSecureToken() (string, error) {
|
||||||
|
b := make([]byte, 16)
|
||||||
|
if _, err := rand.Read(b); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%x", b), nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Go's Built-in Security Features
|
||||||
|
|
||||||
|
Go includes several tools and features to help you write more secure and reliable code.
|
||||||
|
|
||||||
|
### Race Detector
|
||||||
|
|
||||||
|
Race conditions in concurrent code can lead to unpredictable behavior and security vulnerabilities. Go's race detector helps identify these issues at runtime.
|
||||||
|
|
||||||
|
To use the race detector, use the `-race` flag:
|
||||||
|
```bash
|
||||||
|
go test -race ./...
|
||||||
|
go run -race main.go
|
||||||
|
go build -race -o myapp
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example of a race condition:**
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
counter := 0
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
// This line causes a race condition.
|
||||||
|
// Multiple goroutines are reading and writing to `counter` concurrently.
|
||||||
|
counter++
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
fmt.Println("Final counter:", counter)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Running this with `go run -race main.go` will produce a detailed report.
|
||||||
|
|
||||||
|
**Fixing the race condition with a mutex:**
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
var mu sync.Mutex
|
||||||
|
counter := 0
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
mu.Lock()
|
||||||
|
counter++
|
||||||
|
mu.Unlock()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
fmt.Println("Final counter:", counter)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fuzz Testing
|
||||||
|
|
||||||
|
Fuzzing is an automated testing technique that provides randomized inputs to a function to uncover bugs, crashes, and potential security vulnerabilities. Instead of relying on predefined test cases, a fuzzing engine continuously generates new inputs based on the code's behavior, helping to discover edge cases that developers might miss.
|
||||||
|
|
||||||
|
#### Native Fuzzing (Go 1.18+)
|
||||||
|
|
||||||
|
As of Go 1.18, fuzzing is a first-class citizen in the Go toolchain. It is built into the `go test` command and provides a powerful way to test your code for robustness. For a detailed walkthrough, refer to the [official Go fuzzing tutorial](https://go.dev/doc/tutorial/fuzz).
|
||||||
|
|
||||||
|
A fuzz test is a function named `FuzzXxx` that accepts a `*testing.F`. This object is used to manage the fuzzing process.
|
||||||
|
|
||||||
|
1. **Seed Corpus**: You provide an initial set of interesting inputs, called the "seed corpus," using `f.Add()`. The fuzzing engine uses these as a starting point.
|
||||||
|
2. **Fuzz Target**: You define a "fuzz target" function using `f.Fuzz()`. This function receives the testing object `*testing.T` and the randomly generated input arguments. Inside this function, you write the logic to test your code with the given inputs.
|
||||||
|
|
||||||
|
**Example: Fuzzing a String Reversal Function**
|
||||||
|
|
||||||
|
Let's expand on the previous `reverse` function example. A good property to test is that reversing a string twice should yield the original string.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
func reverse(s string) (string, error) {
|
||||||
|
if !utf8.ValidString(s) {
|
||||||
|
return s, fmt.Errorf("input is not valid UTF-8")
|
||||||
|
}
|
||||||
|
runes := []rune(s)
|
||||||
|
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
runes[i], runes[j] = runes[j], runes[i]
|
||||||
|
}
|
||||||
|
return string(runes), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzReverse(f *testing.F) {
|
||||||
|
testcases := []string{"Hello, world", " ", "!12345"}
|
||||||
|
for _, tc := range testcases {
|
||||||
|
f.Add(tc) // Use f.Add to provide a seed corpus
|
||||||
|
}
|
||||||
|
f.Fuzz(func(t *testing.T, orig string) {
|
||||||
|
rev, err1 := reverse(orig)
|
||||||
|
if err1 != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
doubleRev, err2 := reverse(rev)
|
||||||
|
if err2 != nil {
|
||||||
|
t.Fatalf("Failed to reverse the reversed string: %v", err2)
|
||||||
|
}
|
||||||
|
if orig != doubleRev {
|
||||||
|
t.Errorf("Before: %q, after: %q", orig, doubleRev)
|
||||||
|
}
|
||||||
|
if utf8.ValidString(orig) && !utf8.ValidString(rev) {
|
||||||
|
t.Errorf("Reverse produced invalid UTF-8 string %q", rev)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To run the fuzz test, use the `-fuzz` flag with `go test`. The fuzzer will run indefinitely until it finds a failing input or is manually stopped.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -fuzz=FuzzReverse
|
||||||
|
```
|
||||||
|
|
||||||
|
When a failing input is found, it is saved to a file in the `testdata/fuzz/<FuzzTestName>` directory, making it easy to reproduce the failure.
|
||||||
|
|
||||||
|
#### `go-fuzz`
|
||||||
|
|
||||||
|
Before native fuzzing was introduced, the Go ecosystem relied on the pioneering `go-fuzz` tool by Dmitry Vyukov. This tool heavily influenced the official implementation and has an impressive track record of finding hundreds of bugs in various Go projects.
|
||||||
|
|
||||||
|
`go-fuzz` works differently from the native implementation:
|
||||||
|
|
||||||
|
- It requires installing separate command-line tools: `go-fuzz-build` and `go-fuzz`.
|
||||||
|
- The fuzz target function has a specific signature: `func Fuzz(data []byte) int`. It takes a byte slice and returns an integer indicating the quality of the input (1 for interesting inputs, 0 for others).
|
||||||
|
|
||||||
|
**Example: Fuzzing with `go-fuzz`**
|
||||||
|
|
||||||
|
```go
|
||||||
|
// This code is for illustration with go-fuzz
|
||||||
|
// and would be in a separate file, e.g., fuzz.go
|
||||||
|
|
||||||
|
package mypackage
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func Fuzz(data []byte) int {
|
||||||
|
s := string(data)
|
||||||
|
rev, err := reverse(s)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
doubleRev, err := reverse(rev)
|
||||||
|
if err != nil {
|
||||||
|
// This indicates a bug found by go-fuzz
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if s != doubleRev {
|
||||||
|
panic(fmt.Sprintf("orig: %q, doubleRev: %q", s, doubleRev))
|
||||||
|
}
|
||||||
|
return 1 // The input is interesting
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
While native fuzzing is the standard for modern Go projects, `go-fuzz` remains a valuable tool and a significant part of Go's security history.
|
||||||
|
|
||||||
|
For more information, see the official Go documentation on [fuzzing](https://go.dev/doc/security/fuzz/) and the [`go-fuzz` repository](https://github.com/dvyukov/go-fuzz).
|
||||||
|
|
||||||
|
|
||||||
|
### Go Vet
|
||||||
|
|
||||||
|
The `go vet` command examines Go source code and reports suspicious constructs. It can catch a variety of subtle bugs.
|
||||||
|
|
||||||
|
Run it on your project with:
|
||||||
|
```bash
|
||||||
|
go vet ./...
|
||||||
|
```
|
||||||
|
For example, `go vet` can warn about passing a lock by value, which can lead to subtle concurrency bugs.
|
||||||
|
|
||||||
|
## Common Go Mistakes
|
||||||
|
|
||||||
|
Certain programming patterns in Go can lead to subtle bugs that have security implications. Being aware of these common mistakes, as documented in the [Go Wiki](https://go.dev/wiki/CommonMistakes), can help you write more robust and secure code.
|
||||||
|
|
||||||
|
### Using Goroutines on Loop Iterator Variables (pre-Go 1.22)
|
||||||
|
|
||||||
|
A classic mistake in Go versions before 1.22 involves using loop iterator variables within a goroutine's closure. The loop variable is a single variable that is reused for each iteration. If a goroutine in the loop refers to this variable, it will likely observe the value of the variable from the *last* iteration, because the goroutines may not start executing until after the loop has completed.
|
||||||
|
|
||||||
|
**Incorrect Usage:**
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
values := []string{"a", "b", "c"}
|
||||||
|
|
||||||
|
for _, v := range values {
|
||||||
|
wg.Add(1)
|
||||||
|
// This closure captures the loop variable `v`.
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
// By the time this runs, the loop is likely finished,
|
||||||
|
// and `v` will hold the last value, "c".
|
||||||
|
fmt.Println(v)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The output will likely be:
|
||||||
|
```text
|
||||||
|
c
|
||||||
|
c
|
||||||
|
c
|
||||||
|
```
|
||||||
|
|
||||||
|
**Correct Usage:**
|
||||||
|
|
||||||
|
To fix this, you should pass the loop variable as an argument to the goroutine. This creates a copy of the variable for each iteration.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
values := []string{"a", "b", "c"}
|
||||||
|
|
||||||
|
for _, v := range values {
|
||||||
|
wg.Add(1)
|
||||||
|
// Pass `v` as an argument to the closure.
|
||||||
|
go func(val string) {
|
||||||
|
defer wg.Done()
|
||||||
|
fmt.Println(val)
|
||||||
|
}(v) // The value of `v` is evaluated at the time of the call.
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
This version will correctly print `a`, `b`, and `c` (in a non-deterministic order).
|
||||||
|
|
||||||
|
**Note on Go 1.22 and later:** This behavior has been changed in Go 1.22. In newer versions, the loop variable is given a fresh binding for each iteration, so the original "incorrect" code now works as expected. However, it is still crucial to understand this behavior, as you may encounter it in older codebases.
|
||||||
|
|
||||||
|
## Security Checklist
|
||||||
|
|
||||||
|
Here is a high-level checklist to keep in mind during development:
|
||||||
|
|
||||||
|
- [ ] **Dependency Management**: Keep `go.mod` tidy and dependencies up-to-date.
|
||||||
|
- [ ] **Vulnerability Scanning**: Regularly run `govulncheck` in your CI pipeline.
|
||||||
|
- [ ] **Input Validation**: Validate and sanitize all user-provided input.
|
||||||
|
- [ ] **Parameterized Queries**: Use parameterized queries to prevent SQL injection.
|
||||||
|
- [ ] **Contextual Escaping**: Use `html/template` for web output to prevent XSS.
|
||||||
|
- [ ] **Path Sanitization**: Clean file paths from user input to prevent directory traversal.
|
||||||
|
- [ ] **Secure Command Execution**: Use `os/exec` with separate arguments to prevent command injection.
|
||||||
|
- [ ] **SAST**: Integrate `gosec` into your development workflow.
|
||||||
|
- [ ] **Concurrency**: Use the `-race` flag during testing to detect race conditions.
|
||||||
|
- [ ] **Fuzz Testing**: Write fuzz tests for functions that handle complex, untrusted input.
|
||||||
|
- [ ] **Least Privilege**: Run applications with the minimum required permissions.
|
||||||
|
- [ ] **Secrets Management**: Do not hardcode secrets. Use environment variables or a secrets management system.
|
||||||
|
|
||||||
|
## Further Reading and References
|
||||||
|
|
||||||
|
* **[Go Security Best Practices](https://go.dev/doc/security/best-practices)**: Official documentation from the Go team.
|
||||||
|
* **[OWASP Go Secure Coding Practices Guide](https://github.com/OWASP/Go-SCP)**: A comprehensive guide based on OWASP principles.
|
||||||
|
* **[Go Vulnerability Management](https://go.dev/doc/security/vuln/)**: Information about Go's approach to vulnerability management.
|
||||||
|
* **[gosec](https://github.com/securego/gosec)**: A popular static analysis tool for Go.
|
||||||
|
|
||||||
|
By following these best practices and utilizing the tools available in the Go ecosystem, you can significantly improve the security and reliability of your applications.
|
||||||
@ -11,11 +11,13 @@ Secure software development is a practice that integrates security consideration
|
|||||||
|
|
||||||
- [Containers](./containers.md) - Best practices for securing Docker containers, covering the entire lifecycle from building hardened, minimal images to securing the runtime environment and managing data safely.
|
- [Containers](./containers.md) - Best practices for securing Docker containers, covering the entire lifecycle from building hardened, minimal images to securing the runtime environment and managing data safely.
|
||||||
- [DNS](./dns.md) - Explains how to enhance online privacy and security by using secure DNS resolvers, covering home network solutions like Pi-hole and public resolvers like Quad9.
|
- [DNS](./dns.md) - Explains how to enhance online privacy and security by using secure DNS resolvers, covering home network solutions like Pi-hole and public resolvers like Quad9.
|
||||||
|
- [Go](./go.md) - A comprehensive guide to Go security, covering vulnerability management, secure coding practices, static analysis, and Go's built-in security features.
|
||||||
- [Kubernetes](./kubernetes.md) - A comprehensive guide to Kubernetes security, detailing how to secure control plane components, implement network policies, harden workloads with admission controllers, and manage secrets and data.
|
- [Kubernetes](./kubernetes.md) - A comprehensive guide to Kubernetes security, detailing how to secure control plane components, implement network policies, harden workloads with admission controllers, and manage secrets and data.
|
||||||
- [Linux](./linux.md) - A baseline for hardening Linux systems, focusing on user and access management, automated patching, filesystem encryption, network security with host-based firewalls, and logging.
|
- [Linux](./linux.md) - A baseline for hardening Linux systems, focusing on user and access management, automated patching, filesystem encryption, network security with host-based firewalls, and logging.
|
||||||
- [macOS](./macos.md) - Actionable guidance for securing corporate Macs by leveraging native platform features like FileVault, Gatekeeper, and System Integrity Protection, enforced through a Mobile Device Management (MDM) solution.
|
- [macOS](./macos.md) - Actionable guidance for securing corporate Macs by leveraging native platform features like FileVault, Gatekeeper, and System Integrity Protection, enforced through a Mobile Device Management (MDM) solution.
|
||||||
- [Organization](./organization.md) - Outlines a holistic security program, defining core pillars like Identity and Access Management (IAM), Application Security (AppSec), and Incident Response, with maturity milestones and checklists for governance.
|
- [Organization](./organization.md) - Outlines a holistic security program, defining core pillars like Identity and Access Management (IAM), Application Security (AppSec), and Incident Response, with maturity milestones and checklists for governance.
|
||||||
- [Passwords](./passwords.md) - Provides guidance on creating strong, secure passwords, explaining the importance of length, complexity, and uniqueness, and recommends using password managers and two-factor authentication.
|
- [Passwords](./passwords.md) - Provides guidance on creating strong, secure passwords, explaining the importance of length, complexity, and uniqueness, and recommends using password managers and two-factor authentication.
|
||||||
- [Pentests](./pentests.md) - A pragmatic guide to penetration testing methodology, covering planning, reconnaissance, exploitation, and reporting for web applications, APIs, and networks, aligned with OWASP standards.
|
- [Pentests](./pentests.md) - A pragmatic guide to penetration testing methodology, covering planning, reconnaissance, exploitation, and reporting for web applications, APIs, and networks, aligned with OWASP standards.
|
||||||
|
- [React](./react.md) - Covers security best practices for React applications, including mitigating XSS, managing third-party package vulnerabilities, and specific considerations for Next.js like data security and securing Server Actions.
|
||||||
- [Web Development](./web-development.md) - Engineering-focused best practices for building secure web applications, covering the secure SDLC, defense-in-depth principles, and specific controls for mitigating common vulnerabilities like XSS, SQLi, and CSRF.
|
- [Web Development](./web-development.md) - Engineering-focused best practices for building secure web applications, covering the secure SDLC, defense-in-depth principles, and specific controls for mitigating common vulnerabilities like XSS, SQLi, and CSRF.
|
||||||
- [Windows](./windows.md) - A high-level guide to hardening Windows security, centered on applying comprehensive security templates and scripts to reduce the attack surface, enforce strong policies, and leverage built-in controls like Defender and BitLocker.
|
- [Windows](./windows.md) - A high-level guide to hardening Windows security, centered on applying comprehensive security templates and scripts to reduce the attack surface, enforce strong policies, and leverage built-in controls like Defender and BitLocker.
|
||||||
|
|||||||
337
foomo/docs/security/react.md
Normal file
337
foomo/docs/security/react.md
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
---
|
||||||
|
title: React & Next.js Security
|
||||||
|
slug: /security/react
|
||||||
|
authors: [gemini]
|
||||||
|
---
|
||||||
|
|
||||||
|
# React & Next.js Security
|
||||||
|
|
||||||
|
Securing React applications involves understanding common vulnerabilities and leveraging the framework's features, as well as best practices for the ecosystem. This guide covers general React security and then dives into specifics for Next.js.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [General React Security](#general-react-security)
|
||||||
|
- [Cross-Site Scripting (XSS)](#cross-site-scripting-xss)
|
||||||
|
- [Third-Party Package Vulnerabilities](#third-party-package-vulnerabilities)
|
||||||
|
- [API Security](#api-security)
|
||||||
|
- [Server-Side Rendering (SSR)](#server-side-rendering-ssr)
|
||||||
|
- [Cross-Site Request Forgery (CSRF)](#cross-site-request-forgery-csrf)
|
||||||
|
- [Other Vulnerabilities](#other-vulnerabilities)
|
||||||
|
- [Next.js Security](#nextjs-security)
|
||||||
|
- [Data Security](#data-security)
|
||||||
|
- [Authentication](#authentication)
|
||||||
|
- [Server Actions](#server-actions)
|
||||||
|
- [Sources](#sources)
|
||||||
|
- [Security Checklist](#security-checklist)
|
||||||
|
- [1. Package Manager](#1-package-manager)
|
||||||
|
- [2. Dependencies](#2-dependencies)
|
||||||
|
- [3. Data Validation and Sanitization](#3-data-validation-and-sanitization)
|
||||||
|
- [4. Avoiding Code Exposure](#4-avoiding-code-exposure)
|
||||||
|
- [5. Centralize Security Functions](#5-centralize-security-functions)
|
||||||
|
- [6. Security Headers](#6-security-headers)
|
||||||
|
- [7. Code Editor](#7-code-editor)
|
||||||
|
- [8. Rate Limiting and Security Audits](#8-rate-limiting-and-security-audits)
|
||||||
|
- [9. Third-Party Security Scanners](#9-third-party-security-scanners)
|
||||||
|
|
||||||
|
## General React Security
|
||||||
|
|
||||||
|
### Cross-Site Scripting (XSS)
|
||||||
|
|
||||||
|
React helps mitigate XSS attacks by automatically escaping content rendered in JSX. However, there are still ways vulnerabilities can be introduced, especially with DOM-based XSS where malicious code is injected through user input.
|
||||||
|
|
||||||
|
- **`dangerouslySetInnerHTML`**: This prop should be avoided as it bypasses React's protection. If you must use it, ensure the HTML content is sanitized.
|
||||||
|
|
||||||
|
*Vulnerable Example:*
|
||||||
|
```jsx
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function VulnerableComponent({ userInput }) {
|
||||||
|
return <div dangerouslySetInnerHTML={{ __html: userInput }} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Malicious input:
|
||||||
|
// const maliciousInput = '<img src=x onerror="alert(\'XSS Attack!\')" />';
|
||||||
|
// <VulnerableComponent userInput={maliciousInput} />
|
||||||
|
```
|
||||||
|
|
||||||
|
*Secure Example (with DOMPurify):*
|
||||||
|
```jsx
|
||||||
|
import React from 'react';
|
||||||
|
import DOMPurify from 'dompurify';
|
||||||
|
|
||||||
|
function SafeComponent({ userInput }) {
|
||||||
|
const sanitizedHTML = DOMPurify.sanitize(userInput);
|
||||||
|
return <div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Malicious input is now sanitized and the script will not execute.
|
||||||
|
// const maliciousInput = '<img src=x onerror="alert(\'XSS Attack!\')" />';
|
||||||
|
// <SafeComponent userInput={maliciousInput} />
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Direct DOM Access**: Using `refs` to directly manipulate the DOM with `innerHTML` can bypass React's security mechanisms.
|
||||||
|
|
||||||
|
*Vulnerable Example:*
|
||||||
|
```jsx
|
||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
function VulnerableDOMComponent({ htmlContent }) {
|
||||||
|
const divRef = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (divRef.current) {
|
||||||
|
divRef.current.innerHTML = htmlContent;
|
||||||
|
}
|
||||||
|
}, [htmlContent]);
|
||||||
|
|
||||||
|
return <div ref={divRef} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Malicious input:
|
||||||
|
// const maliciousInput = '<img src=x onerror="alert(\'XSS with refs!\')" />';
|
||||||
|
// <VulnerableDOMComponent htmlContent={maliciousInput} />
|
||||||
|
```
|
||||||
|
To fix this, avoid using `innerHTML` and use declarative React rendering. If you must insert HTML, use `dangerouslySetInnerHTML` with a sanitization library like `DOMPurify`.
|
||||||
|
|
||||||
|
- **URL Schemes**: Dynamic URLs in `href` attributes can be exploited with `javascript:` schemes.
|
||||||
|
|
||||||
|
*Vulnerable Example:*
|
||||||
|
```jsx
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function VulnerableLink({ userUrl, linkText }) {
|
||||||
|
return <a href={userUrl}>{linkText}</a>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Malicious URL:
|
||||||
|
// const maliciousUrl = 'javascript:alert("XSS via URL!")';
|
||||||
|
// <VulnerableLink userUrl={maliciousUrl} linkText="Click me" />
|
||||||
|
```
|
||||||
|
*Secure Example:*
|
||||||
|
Validate and sanitize all URLs to ensure they use safe protocols like `http:` or `https:`.
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function SafeLink({ userUrl, linkText }) {
|
||||||
|
const sanitizedUrl = userUrl.startsWith('javascript:') ? '#' : userUrl;
|
||||||
|
// For a robust solution, use a library to validate the URL protocol.
|
||||||
|
return <a href={sanitizedUrl}>{linkText}</a>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Third-Party Package Vulnerabilities
|
||||||
|
|
||||||
|
Modern applications rely heavily on third-party packages.
|
||||||
|
|
||||||
|
- **Audit Dependencies**: Regularly run `npm audit` or `yarn audit` to identify and fix known vulnerabilities in your dependencies.
|
||||||
|
```bash
|
||||||
|
npm audit
|
||||||
|
# or
|
||||||
|
yarn audit
|
||||||
|
```
|
||||||
|
- **Keep Packages Updated**: Use tools like Dependabot to automatically create pull requests for dependency updates.
|
||||||
|
|
||||||
|
### API Security
|
||||||
|
|
||||||
|
- **Authentication & Authorization**: Implement robust authentication and authorization on your backend APIs.
|
||||||
|
- **SQL Injection (SQLi)**: While SQLi is a backend vulnerability, the frontend plays a role in how data is sent to the API. Ensure that your frontend code does not encourage or enable SQLi attacks. For example, avoid constructing SQL-like queries on the client side. Always rely on the backend to properly sanitize and parameterize all incoming data before it's used in database queries.
|
||||||
|
- **Environment Variables**: Never hardcode API keys or other secrets in your frontend code. Use environment variables (e.g., `.env.local`) and prefix them with `NEXT_PUBLIC_` in Next.js if they need to be exposed to the browser. Keys without the prefix are only available on the server-side.
|
||||||
|
|
||||||
|
In `.env.local`:
|
||||||
|
```env
|
||||||
|
API_KEY=secret_value
|
||||||
|
NEXT_PUBLIC_ANALYTICS_ID=public_value
|
||||||
|
```
|
||||||
|
|
||||||
|
In a server-side file (like a Server Component or Route Handler):
|
||||||
|
```javascript
|
||||||
|
const apiKey = process.env.API_KEY; // "secret_value"
|
||||||
|
const analyticsId = process.env.NEXT_PUBLIC_ANALYTICS_ID; // "public_value"
|
||||||
|
```
|
||||||
|
In a client-side file (like a Client Component):
|
||||||
|
```javascript
|
||||||
|
// const apiKey = process.env.API_KEY; // undefined
|
||||||
|
const analyticsId = process.env.NEXT_PUBLIC_ANALYTICS_ID; // "public_value"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Server-Side Rendering (SSR)
|
||||||
|
|
||||||
|
When using SSR, ensure that any data included in the initial server-rendered page is properly sanitized, especially if it includes user-generated content. This prevents XSS during the client-side hydration phase.
|
||||||
|
|
||||||
|
### Cross-Site Request Forgery (CSRF)
|
||||||
|
|
||||||
|
A CSRF attack tricks a victim into submitting a malicious request. It inherits the identity and privileges of the victim to perform an undesired function on their behalf. For instance, a CSRF attack could force a user to transfer funds or change their email address without their knowledge.
|
||||||
|
|
||||||
|
To prevent CSRF, you should use anti-CSRF tokens. These are unique, secret, and unpredictable values generated by the server and sent to the client. The client includes this token in subsequent requests. The server validates the token before processing the request.
|
||||||
|
|
||||||
|
### Other Vulnerabilities
|
||||||
|
|
||||||
|
- **Zip Slip**: This vulnerability occurs when handling zip files. An attacker can craft a malicious archive that, when extracted, overwrites files in the filesystem. If your application handles file uploads, particularly archives, use a library that is not vulnerable to path traversal to extract files.
|
||||||
|
|
||||||
|
- **Broken Authentication**: This is a broad category of vulnerabilities that can occur when authentication and session management are not handled correctly. Common issues include weak passwords, session tokens being exposed in URLs, and improper invalidation of sessions after logout. Always use secure, well-vetted authentication libraries and follow best practices for session management.
|
||||||
|
|
||||||
|
- **Distributed Denial of Service (DDoS)**: While primarily a backend and infrastructure concern, your frontend application can be a target. DDoS attacks aim to make your application unavailable to users by overwhelming it with traffic. Implementing rate limiting on your APIs and using services that provide DDoS protection are common mitigation strategies.
|
||||||
|
|
||||||
|
## Next.js Security
|
||||||
|
|
||||||
|
Next.js introduces its own security considerations, especially with the introduction of Server Components and Server Actions.
|
||||||
|
|
||||||
|
### Data Security
|
||||||
|
|
||||||
|
Next.js provides mechanisms to prevent sensitive data from being leaked to the client.
|
||||||
|
|
||||||
|
- **Server Components**: By default, components in the `app` directory are Server Components. They run only on the server, so sensitive logic and data fetching can be kept out of the client-side bundle.
|
||||||
|
- **`server-only` and `client-only` packages**: You can use these packages to ensure that a module is only ever imported into a Server or Client Component, respectively. This prevents accidentally including server-side code on the client.
|
||||||
|
```javascript
|
||||||
|
import 'server-only';
|
||||||
|
|
||||||
|
export async function getDataFromDatabase() {
|
||||||
|
// This function can only be imported in Server Components.
|
||||||
|
// Attempting to import it in a Client Component will result in a build error.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Data Tainting**: Next.js has experimental support for tainting objects to prevent them from being passed from the server to the client. This can be used to protect sensitive data.
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
Next.js provides a comprehensive guide on authentication.
|
||||||
|
|
||||||
|
- **Middleware**: Use Middleware to protect routes by running code before a request is completed. You can check for a valid session and redirect unauthenticated users.
|
||||||
|
|
||||||
|
Example `middleware.ts`:
|
||||||
|
```typescript
|
||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import type { NextRequest } from 'next/server';
|
||||||
|
|
||||||
|
export function middleware(request: NextRequest) {
|
||||||
|
const sessionCookie = request.cookies.get('session');
|
||||||
|
|
||||||
|
if (!sessionCookie) {
|
||||||
|
return NextResponse.redirect(new URL('/login', request.url));
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
matcher: '/dashboard/:path*',
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Data Access Layer (DAL)**: Centralize data fetching logic in a DAL. This allows you to consistently apply authentication and authorization checks before accessing or modifying data.
|
||||||
|
- **Data Transfer Objects (DTOs)**: Return only the necessary data from your API endpoints or data layers. This prevents accidentally exposing sensitive information, like password hashes, to the client.
|
||||||
|
- **Auth in different contexts**:
|
||||||
|
- **Server Components**: Conditionally render UI based on user roles.
|
||||||
|
- **Layouts**: Avoid performing auth checks in layouts that don't re-render on navigation, as the session might not be re-validated.
|
||||||
|
- **Server Actions**: Treat them as public API endpoints. Always verify that the user has the required permissions to perform the action.
|
||||||
|
- **Route Handlers**: Similar to Server Actions, they should be treated as API endpoints and require authentication and authorization checks.
|
||||||
|
|
||||||
|
### Server Actions
|
||||||
|
|
||||||
|
Server Actions are functions that execute on the server. They must be secured properly.
|
||||||
|
|
||||||
|
- **Permissions Check**: Always validate the user's session and permissions at the beginning of a Server Action.
|
||||||
|
- **Input Validation**: Sanitize and validate all input received from the client to prevent vulnerabilities.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
'use server';
|
||||||
|
|
||||||
|
import { auth } from '@/lib/auth'; // Your authentication logic
|
||||||
|
import { db } from '@/lib/db';
|
||||||
|
|
||||||
|
export async function updatePost(formData: FormData) {
|
||||||
|
const session = await auth();
|
||||||
|
|
||||||
|
if (!session?.user) {
|
||||||
|
throw new Error('Unauthorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
const postId = formData.get('id');
|
||||||
|
const content = formData.get('content');
|
||||||
|
|
||||||
|
// Add input validation here (e.g., with Zod)
|
||||||
|
|
||||||
|
// Check if user has permission to edit this specific post
|
||||||
|
const post = await db.post.findUnique({ where: { id: postId } });
|
||||||
|
if (post.authorId !== session.user.id) {
|
||||||
|
throw new Error('Forbidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... proceed with update
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Checklist
|
||||||
|
|
||||||
|
### 1. Package Manager
|
||||||
|
It is also recommended to use `pnpm` over `npm` or `yarn`. `pnpm` is more efficient with disk space and is generally faster. From a security perspective, its non-flat `node_modules` structure provides stricter package isolation, reducing the risk of unauthorized package access. Furthermore, `pnpm` offers advanced security features, such as `minimumReleaseAge`, which can delay the installation of new package versions to protect against supply-chain attacks where malicious code is published and quickly downloaded.
|
||||||
|
|
||||||
|
You can configure this in your `pnpm-workspace.yaml` file:
|
||||||
|
```yaml
|
||||||
|
minimumReleaseAge: 1440 # 1440 minutes = 24 hours
|
||||||
|
```
|
||||||
|
This setting will prevent the installation of any package version that is less than 24 hours old, giving time for malicious packages to be discovered and removed from the registry.
|
||||||
|
|
||||||
|
### 2. Dependencies
|
||||||
|
The first and most important step is to manage your dependencies. This is a significant source of risk, not just in terms of security but also licensing. It is important to keep your dependencies up to date, to make it easier to react when critical security patches are released. Use a tool like Dependabot to get notified about new versions of your dependencies and open pull requests to update them.
|
||||||
|
|
||||||
|
### 3. Data Validation and Sanitization
|
||||||
|
Data validation and sanitization is crucial, especially for incoming data. This is particularly important for server-side code, which interacts with your database. You should never trust data coming from the client, as it can be easily manipulated. It is recommended to use a data access layer to centralize all your database interactions and perform authentication checks before accessing data. For sensitive information, consider using tools like Arcjet to redact or deny requests containing personal data like emails or phone numbers.
|
||||||
|
|
||||||
|
### 4. Avoiding Code Exposure
|
||||||
|
Be mindful of exposing sensitive data or code to the client. Avoid hardcoding API keys or other secrets directly in your code. Instead, use environment variables to store them. For server-side code that should never be exposed to the client, you can use the server-only package, which will throw an error if it's accidentally imported into a client component.
|
||||||
|
|
||||||
|
### 5. Centralize Security Functions
|
||||||
|
A well-organized security strategy involves centralizing key functions. This approach streamlines testing, auditing, and maintenance while reducing the risk of inconsistencies or oversights. Grouping functions by their purpose, for example, creating a data access layer for all database interactions, can improve maintainability and testability.
|
||||||
|
|
||||||
|
### 6. Security Headers
|
||||||
|
Pay attention to the headers you are setting. A Content Security Policy (CSP) is important to guard your Next.js application against various security threats such as cross-site scripting (XSS), clickjacking, and other code injection attacks.
|
||||||
|
|
||||||
|
Implementing a strict CSP in a React application requires careful configuration, especially regarding inline scripts. By default, Create React App embeds a small runtime script inline in `index.html`. To create a secure CSP, you must disable this behavior. You can do this by setting the `INLINE_RUNTIME_CHUNK=false` environment variable in your `.env` file or your build script:
|
||||||
|
```bash
|
||||||
|
INLINE_RUNTIME_CHUNK=false npm run build
|
||||||
|
```
|
||||||
|
Once you've removed inline scripts, you can implement a CSP. The policy can be delivered via a `<meta>` tag in your HTML or through an HTTP header. An HTTP header is generally more flexible.
|
||||||
|
|
||||||
|
Here is an example of a strict CSP that only allows resources from the same origin:
|
||||||
|
```http
|
||||||
|
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self';
|
||||||
|
```
|
||||||
|
In some cases, you may need to allow third-party scripts or styles. If you can't avoid inline scripts, you can use a nonce-based approach. A nonce is a randomly generated string that is unique for each request. You include the nonce in your CSP header and in the script tag.
|
||||||
|
|
||||||
|
Example of a nonce-based policy:
|
||||||
|
```http
|
||||||
|
Content-Security-Policy: script-src 'self' 'nonce-rAnd0m';
|
||||||
|
```
|
||||||
|
And in your HTML:
|
||||||
|
```html
|
||||||
|
<script nonce="rAnd0m"> // this value needs to be randomized!
|
||||||
|
// Inline script
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
This ensures that only the scripts with the correct nonce will be executed, blocking any injected scripts that do not have the nonce.
|
||||||
|
|
||||||
|
### 7. Code Editor
|
||||||
|
Your code editor can also help you catch security issues before you commit. Enable linting tools like ESLint to catch syntax errors, style inconsistencies, and a potential security issues early on. Tools like TruffleHog can also be used to detect secrets that may have been accidentally included in your code.
|
||||||
|
|
||||||
|
### 9. Third-Party Security Scanners
|
||||||
|
In addition to the tools and practices mentioned above, it is highly recommended to use third-party security scanners to continuously monitor your code for vulnerabilities. These tools can be integrated into your development workflow and provide real-time feedback.
|
||||||
|
|
||||||
|
- **[Snyk](http://snyk.io/code-checker/javascript/)**: Snyk offers a free code checker that can be integrated directly into your IDE. It uses AI to analyze your code for a wide range of security issues, including SQL injection, insecure password handling, and information disclosure. It provides actionable advice to help you fix vulnerabilities quickly.
|
||||||
|
|
||||||
|
- **[OWASP Dependency-Check](https://owasp.org/www-project-dependency-check/)**: This is a Software Composition Analysis (SCA) tool that detects publicly disclosed vulnerabilities within a project’s dependencies. It can be run via a command line interface, Maven plugin, or Jenkins plugin, and it generates reports that link to CVE entries. You can find its source code on [GitHub](https://github.com/dependency-check/DependencyCheck).
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
- [React Native - Security](https://reactnative.dev/docs/security)
|
||||||
|
- [React JS Security Best Practices](https://dev.to/kristiyanvelkov/react-js-security-best-practices-15g7)
|
||||||
|
- [Next.js - Authentication](https://nextjs.org/docs/app/guides/authentication)
|
||||||
|
- [Next.js - Security: Server Components and Server Actions](https://nextjs.org/blog/security-nextjs-server-components-actions)
|
||||||
|
- [Next.js - Data Security](https://nextjs.org/docs/app/guides/data-security)
|
||||||
|
- [React XSS Guide: Examples and Prevention](https://www.stackhawk.com/blog/react-xss-guide-examples-and-prevention/)
|
||||||
|
- [pnpm Settings](https://pnpm.io/settings)
|
||||||
|
- [Snyk Code Checker for JavaScript](http://snyk.io/code-checker/javascript/)
|
||||||
|
- [OWASP Dependency-Check](https://owasp.org/www-project-dependency-check/)
|
||||||
|
- [OWASP Dependency-Check GitHub Repository](https://github.com/dependency-check/DependencyCheck)
|
||||||
|
- [OWASP France - CSP with React](https://owasp.org/www-chapter-france/event/2020/2020-09-10_csp_with_react.pdf)
|
||||||
@ -1,8 +1,7 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
// Note: type annotations allow type checking and IDEs autocompletion
|
// Note: type annotations allow type checking and IDEs autocompletion
|
||||||
|
|
||||||
// const lightCodeTheme = require('prism-react-renderer/themes/github');
|
const {themes} = require('prism-react-renderer');
|
||||||
// const darkCodeTheme = require('prism-react-renderer/themes/dracula');
|
|
||||||
|
|
||||||
/** @type {import('@docusaurus/types').Config} */
|
/** @type {import('@docusaurus/types').Config} */
|
||||||
const config = {
|
const config = {
|
||||||
@ -50,8 +49,8 @@ const config = {
|
|||||||
({
|
({
|
||||||
colorMode: {
|
colorMode: {
|
||||||
defaultMode: 'light',
|
defaultMode: 'light',
|
||||||
disableSwitch: true,
|
disableSwitch: false,
|
||||||
|
respectPrefersColorScheme: true,
|
||||||
},
|
},
|
||||||
algolia: {
|
algolia: {
|
||||||
appId: 'SUATUVZDDM',
|
appId: 'SUATUVZDDM',
|
||||||
@ -172,6 +171,8 @@ const config = {
|
|||||||
// contextualSearch: true,
|
// contextualSearch: true,
|
||||||
// }
|
// }
|
||||||
prism: {
|
prism: {
|
||||||
|
theme: themes.github,
|
||||||
|
darkTheme: themes.dracula,
|
||||||
additionalLanguages: ['go'],
|
additionalLanguages: ['go'],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -15,26 +15,23 @@
|
|||||||
"typecheck": "tsc"
|
"typecheck": "tsc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "^3.0.0",
|
"@docusaurus/core": "^3.8.1",
|
||||||
"@docusaurus/preset-classic": "^3.0.0",
|
"@docusaurus/preset-classic": "^3.8.1",
|
||||||
"@docusaurus/theme-live-codeblock": "^3.0.0",
|
"@docusaurus/theme-live-codeblock": "^3.8.1",
|
||||||
"@docusaurus/theme-search-algolia": "^3.0.0",
|
"@docusaurus/theme-search-algolia": "^3.8.1",
|
||||||
"@mdx-js/react": "^1.6.21",
|
"@mdx-js/react": "^3.0.0",
|
||||||
"@saucelabs/theme-github-codeblock": "0.1.1",
|
"@saucelabs/theme-github-codeblock": "0.1.1",
|
||||||
"@svgr/webpack": "^5.5.0",
|
"clsx": "^2.1.0",
|
||||||
"clsx": "^1.1.1",
|
"prism-react-renderer": "^2.3.1",
|
||||||
"file-loader": "^6.2.0",
|
|
||||||
"prism-react-renderer": "^1.2.1",
|
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-full-screen": "^1.1.0",
|
"react-full-screen": "^1.1.0",
|
||||||
"url": "^0.11.0",
|
"url": "^0.11.0"
|
||||||
"url-loader": "^4.1.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/module-type-aliases": "^3.0.0",
|
"@docusaurus/module-type-aliases": "^3.8.1",
|
||||||
"@tsconfig/docusaurus": "^1.0.4",
|
"@tsconfig/docusaurus": "^2.0.2",
|
||||||
"typescript": "^4.9.5"
|
"typescript": "^5.2.2"
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
@ -49,6 +46,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
}
|
},
|
||||||
|
"packageManager": "yarn@4.9.4"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import React, { useEffect, useState } from "react";
|
|||||||
import { Iframe } from "./Iframe";
|
import { Iframe } from "./Iframe";
|
||||||
import { IsItCool } from "./IsItCool";
|
import { IsItCool } from "./IsItCool";
|
||||||
|
|
||||||
import Highlight, { defaultProps } from "prism-react-renderer";
|
import { Highlight, defaultProps } from "prism-react-renderer";
|
||||||
import Link from "@docusaurus/Link";
|
import Link from "@docusaurus/Link";
|
||||||
|
|
||||||
export const GoPlayground = (props: { id: string; proportion: number }) => {
|
export const GoPlayground = (props: { id: string; proportion: number }) => {
|
||||||
|
|||||||
@ -12,6 +12,10 @@
|
|||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme='dark']) .features .feature {
|
||||||
|
background-color: #303030;
|
||||||
|
}
|
||||||
|
|
||||||
.featureSvg {
|
.featureSvg {
|
||||||
height: 200px;
|
height: 200px;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
|
|||||||
@ -15,11 +15,14 @@ export const IsItCool = (props: {
|
|||||||
topic: string;
|
topic: string;
|
||||||
id: string;
|
id: string;
|
||||||
}) => {
|
}) => {
|
||||||
const [isCool, setIsCool] = useState(getISCool(props.id));
|
const [isCool, setIsCool] = useState(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("well it is cool",props.id, {isCool, SSR})
|
if (localStorage.getItem(props.id) === yes) {
|
||||||
}, [isCool, SSR]);
|
setIsCool(true);
|
||||||
if (!isCool) {
|
}
|
||||||
|
}, [props.id]);
|
||||||
|
|
||||||
|
if (SSR || !isCool) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import Highlight, { defaultProps } from "prism-react-renderer";
|
import { Highlight, defaultProps } from "prism-react-renderer";
|
||||||
|
|
||||||
const LZString = (() => {
|
const LZString = (() => {
|
||||||
function o(o, r) {
|
function o(o, r) {
|
||||||
@ -168,7 +168,7 @@ const LZString = (() => {
|
|||||||
export const TypeScriptPlayground = (props: { children: string }) => {
|
export const TypeScriptPlayground = (props: { children: string }) => {
|
||||||
const url =
|
const url =
|
||||||
"https://www.typescriptlang.org/play?#code/" +
|
"https://www.typescriptlang.org/play?#code/" +
|
||||||
escape(LZString.compressToEncodedURIComponent(props.children));
|
encodeURIComponent(LZString.compressToEncodedURIComponent(props.children));
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Highlight {...defaultProps} code={props.children} language="tsx">
|
<Highlight {...defaultProps} code={props.children} language="tsx">
|
||||||
|
|||||||
@ -16,6 +16,16 @@
|
|||||||
--ifm-code-font-size: 95%;
|
--ifm-code-font-size: 95%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html[data-theme='dark'] {
|
||||||
|
--ifm-color-primary: #59a6ee;
|
||||||
|
--ifm-color-primary-dark: #388ad7;
|
||||||
|
--ifm-color-primary-darker: #2984d8;
|
||||||
|
--ifm-color-primary-darkest: #103252;
|
||||||
|
--ifm-color-primary-light: #1b558c;
|
||||||
|
--ifm-color-primary-lighter: #0d2c48;
|
||||||
|
--ifm-color-primary-lightest: #0a2135;
|
||||||
|
}
|
||||||
|
|
||||||
.docusaurus-highlight-code-line {
|
.docusaurus-highlight-code-line {
|
||||||
background-color: rgba(0, 0, 0, 0.1);
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
23064
foomo/yarn.lock
23064
foomo/yarn.lock
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"devDependencies": {
|
|
||||||
"@docusaurus/module-type-aliases": "^3.0.0",
|
|
||||||
"@tsconfig/docusaurus": "^1.0.6",
|
|
||||||
"typescript": "^4.9.5"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@docusaurus/core": "^3.0.0",
|
|
||||||
"@docusaurus/preset-classic": "^3.0.0",
|
|
||||||
"@docusaurus/theme-live-codeblock": "^3.0.0",
|
|
||||||
"@docusaurus/theme-search-algolia": "^3.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user