From f725585f32a02558b0bd03c4328c516687790515 Mon Sep 17 00:00:00 2001 From: Joseline Date: Thu, 18 Sep 2025 12:06:28 +0200 Subject: [PATCH 01/19] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c2bec0368b..28a4c0d0af 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,6 @@ go build -o notely && ./notely *This starts the server in non-database mode.* It will serve a simple webpage at `http://localhost:8080`. You do *not* need to set up a database or any interactivity on the webpage yet. Instructions for that will come later in the course! +joselinekahunde's +version of Boot.dev's Notely app. +update README with my version note From c10a143527dd27d47581b455d2461e213aaa5a4c Mon Sep 17 00:00:00 2001 From: joseline kahunde Date: Tue, 23 Sep 2025 11:11:23 +0200 Subject: [PATCH 02/19] Add my version line to README :wq# Changes to be committed: --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 28a4c0d0af..42be104d1a 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,10 @@ go build -o notely && ./notely *This starts the server in non-database mode.* It will serve a simple webpage at `http://localhost:8080`. You do *not* need to set up a database or any interactivity on the webpage yet. Instructions for that will come later in the course! +<<<<<<< HEAD joselinekahunde's version of Boot.dev's Notely app. update README with my version note +======= +Joseline Kahunde's version of Boot.dev's Notely app. +>>>>>>> c86990f (Add my version line to README) From 407e971ce98336ff5252f3abc9c63666c10066f2 Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Tue, 23 Sep 2025 13:22:05 +0200 Subject: [PATCH 03/19] Clean up README: remove conflict markers and keep final line --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 42be104d1a..52b2a9dd5e 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,7 @@ go build -o notely && ./notely *This starts the server in non-database mode.* It will serve a simple webpage at `http://localhost:8080`. You do *not* need to set up a database or any interactivity on the webpage yet. Instructions for that will come later in the course! -<<<<<<< HEAD -joselinekahunde's -version of Boot.dev's Notely app. -update README with my version note -======= + Joseline Kahunde's version of Boot.dev's Notely app. ->>>>>>> c86990f (Add my version line to README) + + From e55e51e756bda3df447fbc87e5b18def6c9ca178 Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Wed, 24 Sep 2025 00:23:23 +0200 Subject: [PATCH 04/19] Add failing CI workflow (Force Failure) --- .github/workflows/ci.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..c77221ca0e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,24 @@ +name: ci +cat > .github/workflows/ci.yml <<'YAML' +name: ci + +on: + pull_request: + branches: [main] + +jobs: + tests: + name: Tests + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.25.1" + + - name: Force Failure + run: (exit 1) From 7d1576e33c13715e0dc1144d2a088d772dd63192 Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Wed, 24 Sep 2025 01:23:02 +0200 Subject: [PATCH 05/19] CI: print Go version instead of force failure --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c77221ca0e..a3363902ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,5 +20,5 @@ jobs: with: go-version: "1.25.1" - - name: Force Failure - run: (exit 1) + - name: Print Go version + run: go version From 50fa12fa6e9445cb629bce90c7cee7457f95066a Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Wed, 24 Sep 2025 12:52:47 +0200 Subject: [PATCH 06/19] ci: run on pull_request to main; print Go version --- .github/workflows/ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3363902ed..59c84fdd7b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,4 @@ name: ci -cat > .github/workflows/ci.yml <<'YAML' -name: ci on: pull_request: @@ -10,7 +8,6 @@ jobs: tests: name: Tests runs-on: ubuntu-latest - steps: - name: Check out code uses: actions/checkout@v4 @@ -22,3 +19,4 @@ jobs: - name: Print Go version run: go version + From 75e7a2e0dd95d21c83ad705aa7382b5641028f19 Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Thu, 25 Sep 2025 09:50:23 +0200 Subject: [PATCH 07/19] CI: run go test ./... (replace go version step) --- .github/workflows/ci.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 59c84fdd7b..2822b6d5e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,8 @@ name: ci on: + push: + branches: ["**"] # run on every push (helps the grader) pull_request: branches: [main] @@ -15,8 +17,8 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.25.1" - - - name: Print Go version - run: go version + go-version-file: go.mod # stays in sync with your module's Go version + cache: true + - name: Run unit tests + run: go test ./... # <-- replaces "go version" From 2d52a53c7c7b14754bdbee1857f7b645d80f4027 Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Thu, 25 Sep 2025 09:51:39 +0200 Subject: [PATCH 08/19] test: intentional failure to verify CI --- fail_test.go | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 fail_test.go diff --git a/fail_test.go b/fail_test.go new file mode 100644 index 0000000000..512609dff4 --- /dev/null +++ b/fail_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func TestIntentionalFailure(t *testing.T) { + t.Fatalf("intentional failure") +} From 93fe28ae7df8528a259f68eebc1d21e7f754f65d Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Thu, 25 Sep 2025 09:53:34 +0200 Subject: [PATCH 09/19] test: remove intentional failure --- fail_test.go | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 fail_test.go diff --git a/fail_test.go b/fail_test.go deleted file mode 100644 index 512609dff4..0000000000 --- a/fail_test.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import "testing" - -func TestIntentionalFailure(t *testing.T) { - t.Fatalf("intentional failure") -} From cb9e8fbcc6433a8c99359e402a96d1a2a5e34ef1 Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Fri, 26 Sep 2025 09:48:19 +0200 Subject: [PATCH 10/19] ci: run tests with coverage reporting --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2822b6d5e9..3536f00a9f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,5 +20,5 @@ jobs: go-version-file: go.mod # stays in sync with your module's Go version cache: true - - name: Run unit tests - run: go test ./... # <-- replaces "go version" + - name: Run unit tests with coverage + run: go test -cover ./... From bc40923e9d54f1e063ed9928747fa1d25d1ecc05 Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Fri, 26 Sep 2025 09:53:21 +0200 Subject: [PATCH 11/19] fix(ci): correct yaml indentation and add coverage flag --- .github/workflows/ci.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3536f00a9f..1474485e8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,13 +2,12 @@ name: ci on: push: - branches: ["**"] # run on every push (helps the grader) + branches: ["**"] # run on every push (helps the grader) pull_request: branches: [main] jobs: tests: - name: Tests runs-on: ubuntu-latest steps: - name: Check out code @@ -17,8 +16,8 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version-file: go.mod # stays in sync with your module's Go version + go-version-file: go.mod # stays in sync with your module's Go version cache: true - - name: Run unit tests with coverage + - name: Run unit tests with coverage run: go test -cover ./... From eae4a418896b43f6c33cae854801926a24b8cea3 Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Fri, 26 Sep 2025 11:48:44 +0200 Subject: [PATCH 12/19] ci: ensure 'go test -cover ./...' (ASCII hyphen) and push trigger --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1474485e8c..318e6dc30a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,12 +2,13 @@ name: ci on: push: - branches: ["**"] # run on every push (helps the grader) + branches: ["**"] pull_request: branches: [main] jobs: tests: + name: Tests runs-on: ubuntu-latest steps: - name: Check out code @@ -16,7 +17,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version-file: go.mod # stays in sync with your module's Go version + go-version-file: go.mod cache: true - name: Run unit tests with coverage From dcacbe48d9e6026c227969a238e5abaf05e440bb Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Tue, 30 Sep 2025 09:51:59 +0200 Subject: [PATCH 13/19] Add style job with go fmt and staticcheck --- .github/workflows/ci.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 318e6dc30a..1fc3852627 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,15 @@ jobs: with: go-version-file: go.mod cache: true + + - name: Run go fmt + run: test -z "$(go fmt ./...)" + + - name: Install staticcheck + run: go install honnef.co/go/tools/cmd/staticcheck@latest + + - name: Run staticcheck + run: staticcheck ./... - name: Run unit tests with coverage run: go test -cover ./... From 54737d45565f69181cdf4a53ff64296535dc49aa Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Tue, 30 Sep 2025 10:21:37 +0200 Subject: [PATCH 14/19] Introduce unused function to trigger staticcheck failure --- main.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/main.go b/main.go index 19d7366c5f..e71b3190a7 100644 --- a/main.go +++ b/main.go @@ -96,3 +96,7 @@ func main() { log.Printf("Serving on port: %s\n", port) log.Fatal(srv.ListenAndServe()) } +func unused() { + // this function does nothing + // and is called nowhere +} From a119b53b48cf98375d560cb0e607242e486452ec Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Tue, 30 Sep 2025 15:32:18 +0200 Subject: [PATCH 15/19] Remove unused function to fix staticcheck failure --- main.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index e71b3190a7..482572358e 100644 --- a/main.go +++ b/main.go @@ -82,8 +82,7 @@ func main() { v1Router.Post("/users", apiCfg.handlerUsersCreate) v1Router.Get("/users", apiCfg.middlewareAuth(apiCfg.handlerUsersGet)) v1Router.Get("/notes", apiCfg.middlewareAuth(apiCfg.handlerNotesGet)) - v1Router.Post("/notes", apiCfg.middlewareAuth(apiCfg.handlerNotesCreate)) - } + v1Router.Post("/notes", apiCfg.middlewareAuth(apiCfg.handlerNotesCreate)) } v1Router.Get("/healthz", handlerReadiness) @@ -96,7 +95,4 @@ func main() { log.Printf("Serving on port: %s\n", port) log.Fatal(srv.ListenAndServe()) } -func unused() { - // this function does nothing - // and is called nowhere -} + From 4634fe3485f9d8be3adae8a5e1fa6b643d27dad1 Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Tue, 30 Sep 2025 15:37:06 +0200 Subject: [PATCH 16/19] Fix formatting issues for CI --- main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 482572358e..19d7366c5f 100644 --- a/main.go +++ b/main.go @@ -82,7 +82,8 @@ func main() { v1Router.Post("/users", apiCfg.handlerUsersCreate) v1Router.Get("/users", apiCfg.middlewareAuth(apiCfg.handlerUsersGet)) v1Router.Get("/notes", apiCfg.middlewareAuth(apiCfg.handlerNotesGet)) - v1Router.Post("/notes", apiCfg.middlewareAuth(apiCfg.handlerNotesCreate)) } + v1Router.Post("/notes", apiCfg.middlewareAuth(apiCfg.handlerNotesCreate)) + } v1Router.Get("/healthz", handlerReadiness) @@ -95,4 +96,3 @@ func main() { log.Printf("Serving on port: %s\n", port) log.Fatal(srv.ListenAndServe()) } - From d0fa9fa657082dc2d931837634228f990b14196e Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Wed, 1 Oct 2025 15:19:09 +0200 Subject: [PATCH 17/19] Add gosec step to CI workflow --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1fc3852627..8880d2a364 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,3 +31,11 @@ jobs: - name: Run unit tests with coverage run: go test -cover ./... + - name: Install gosec + run: go install github.com/securego/gosec/v2/cmd/gosec@latest + + - name: Add Go bin to PATH + run: echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" + + - name: Run gosec (intentionally fails for first push) + run: gosec ./... From ae0b387de6a7469d9eb524c0b497a5c50aa70e8e Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Thu, 2 Oct 2025 10:55:29 +0200 Subject: [PATCH 18/19] Fix gosec issues: handle writes and add server timeouts --- json.go | 4 +++- main.go | 22 ++++++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/json.go b/json.go index 1e6e7985e1..88b85af8e4 100644 --- a/json.go +++ b/json.go @@ -30,5 +30,7 @@ func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) { return } w.WriteHeader(code) - w.Write(dat) + if _, err := w.Write(dat); err != nil { + log.Printf("warning: response write failed: %v", err) } + diff --git a/main.go b/main.go index 19d7366c5f..ccb4306178 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "embed" "io" "log" + "time" "net/http" "os" @@ -88,11 +89,20 @@ func main() { v1Router.Get("/healthz", handlerReadiness) router.Mount("/v1", v1Router) - srv := &http.Server{ - Addr: ":" + port, - Handler: router, - } + // after: router.Mount("/v1", v1Router) + +srv := &http.Server{ + Addr: ":" + port, + Handler: router, + ReadHeaderTimeout: 10 * time.Second, // fixes G112 + ReadTimeout: 15 * time.Second, + WriteTimeout: 15 * time.Second, + IdleTimeout: 60 * time.Second, +} - log.Printf("Serving on port: %s\n", port) - log.Fatal(srv.ListenAndServe()) +log.Printf("Serving on port: %s\n", port) +if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("server failed: %v", err) } + + From ee34e209943fd7ff9cd0e9a7c5b243ad06a518b9 Mon Sep 17 00:00:00 2001 From: Joseline Kahunde Date: Thu, 2 Oct 2025 11:17:30 +0200 Subject: [PATCH 19/19] Fix security: handle response writes and add server timeouts (gosec clean) --- .gitignore | 1 + json.go | 12 ++++++++---- main.go | 27 +++++++++++++-------------- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 2092f54e78..b62d782e5a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ out .env learn-cicd-starter notely +gosec-report.json diff --git a/json.go b/json.go index 88b85af8e4..77aa881133 100644 --- a/json.go +++ b/json.go @@ -13,9 +13,11 @@ func respondWithError(w http.ResponseWriter, code int, msg string, logErr error) if code > 499 { log.Printf("Responding with 5XX error: %s", msg) } + type errorResponse struct { Error string `json:"error"` } + respondWithJSON(w, code, errorResponse{ Error: msg, }) @@ -23,14 +25,16 @@ func respondWithError(w http.ResponseWriter, code int, msg string, logErr error) func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) { w.Header().Set("Content-Type", "application/json") + dat, err := json.Marshal(payload) if err != nil { - log.Printf("Error marshalling JSON: %s", err) - w.WriteHeader(500) + log.Printf("Error marshalling JSON: %v", err) + w.WriteHeader(http.StatusInternalServerError) return } + w.WriteHeader(code) if _, err := w.Write(dat); err != nil { - log.Printf("warning: response write failed: %v", err) + log.Printf("warning: response write failed: %v", err) + } } - diff --git a/main.go b/main.go index ccb4306178..721626e34d 100644 --- a/main.go +++ b/main.go @@ -5,9 +5,9 @@ import ( "embed" "io" "log" - "time" "net/http" "os" + "time" "github.com/go-chi/chi" "github.com/go-chi/cors" @@ -91,18 +91,17 @@ func main() { router.Mount("/v1", v1Router) // after: router.Mount("/v1", v1Router) -srv := &http.Server{ - Addr: ":" + port, - Handler: router, - ReadHeaderTimeout: 10 * time.Second, // fixes G112 - ReadTimeout: 15 * time.Second, - WriteTimeout: 15 * time.Second, - IdleTimeout: 60 * time.Second, -} + srv := &http.Server{ + Addr: ":" + port, + Handler: router, + ReadHeaderTimeout: 10 * time.Second, // fixes G112 + ReadTimeout: 15 * time.Second, + WriteTimeout: 15 * time.Second, + IdleTimeout: 60 * time.Second, + } -log.Printf("Serving on port: %s\n", port) -if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Fatalf("server failed: %v", err) + log.Printf("Serving on port: %s\n", port) + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("server failed: %v", err) + } } - -