diff --git a/Dockerfile b/Dockerfile index 750d65b..a32e6f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ RUN go mod download COPY main.go . # Build the Go app -RUN CGO_ENABLED=0 GOOS=linux go build -a -o app main.go +RUN CGO_ENABLED=1 GOOS=linux go build -a -o app main.go # Start from debian:bookworm-slim for the release image FROM debian:bookworm-slim diff --git a/Mayhemfile b/Mayhemfile index c17bfb1..2bd4827 100644 --- a/Mayhemfile +++ b/Mayhemfile @@ -1,5 +1,5 @@ -image: ghcr.io/unionfindbee/demo-oneplatform:latest -duration: 60 +image: ghcr.io/unionfindbee/demo-oneplatform:vulnerable +duration: 12000 project: bengutierrez/demo-oneplatform target: mcode diff --git a/go.mod b/go.mod index 28c3c67..1fc04b5 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,8 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect + github.com/mattn/go-sqlite3 v1.14.17 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.8.4 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index d10116b..08f501f 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,10 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= +github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= diff --git a/main.go b/main.go index 4311ce4..5303559 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,10 @@ package main import ( "context" + "database/sql" "encoding/json" "errors" + "fmt" "log" "net/http" "os" @@ -13,6 +15,7 @@ import ( "github.com/google/uuid" "github.com/gorilla/mux" "github.com/gorilla/websocket" + _ "github.com/mattn/go-sqlite3" ) type Weather struct { @@ -29,7 +32,18 @@ var upgrader = websocket.Upgrader{ WriteBufferSize: 1024, } +type User struct { + Email string `json:"email"` + Password string `json:"password"` +} + +var db *sql.DB + func main() { + // Connect to the database + db, _ = sql.Open("sqlite3", "./weather.db") + createTable() + // Create a context that we can cancel ctx, cancel := context.WithCancel(context.Background()) @@ -70,6 +84,21 @@ func main() { log.Println("Server stopped") } +// Create table if it doesn't exist +func createTable() { + query := `CREATE TABLE IF NOT EXISTS weathers ( + id TEXT PRIMARY KEY, + city TEXT, + temperature REAL, + conditions TEXT + );` + + _, err := db.Exec(query) + if err != nil { + log.Fatal(err) + } +} + func createWeather(w http.ResponseWriter, r *http.Request) { var newWeather Weather err := json.NewDecoder(r.Body).Decode(&newWeather) @@ -84,10 +113,19 @@ func createWeather(w http.ResponseWriter, r *http.Request) { return } - // Here we generate a new unique ID newWeather.ID = uuid.New().String() WeatherDB[newWeather.ID] = newWeather + // Dangerous SQL query, opening for SQL injection + query := fmt.Sprintf("INSERT INTO weathers (id, city, temperature, conditions) VALUES ('%s', '%s', %f, '%s')", + newWeather.ID, newWeather.City, newWeather.Temperature, newWeather.Conditions) + + _, err = db.Exec(query) + if err != nil { + http.Error(w, "Error executing SQL query: "+err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) // Set the status before encoding the body json.NewEncoder(w).Encode(newWeather) @@ -134,13 +172,16 @@ func updateWeather(w http.ResponseWriter, r *http.Request) { func deleteWeather(w http.ResponseWriter, r *http.Request) { id := mux.Vars(r)["id"] - if _, ok := WeatherDB[id]; ok { - delete(WeatherDB, id) - w.WriteHeader(http.StatusNoContent) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusNotFound) + weatherToDelete := WeatherDB[id] + + // Here is the null pointer dereference vulnerability: + // Trying to access fields of weatherToDelete which can be nil + log.Printf("Deleting weather for city: %s", weatherToDelete.City) + + // Delete from the map + delete(WeatherDB, id) + + w.WriteHeader(http.StatusNoContent) } func weatherStream(w http.ResponseWriter, r *http.Request) { diff --git a/main_test.go b/main_test.go index 766fc40..9edaa14 100644 --- a/main_test.go +++ b/main_test.go @@ -2,15 +2,32 @@ package main import ( "bytes" + "database/sql" "encoding/json" + "log" "net/http" "net/http/httptest" + "os" "testing" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" ) +func TestMain(m *testing.M) { + var err error + db, err = sql.Open("sqlite3", "./weather_test.db") + if err != nil { + log.Fatal(err) + } + createTable() + + code := m.Run() + + db.Close() + os.Exit(code) +} + func TestCreateWeather(t *testing.T) { router := mux.NewRouter() router.HandleFunc("/weather", createWeather).Methods("POST") diff --git a/weather_test.db b/weather_test.db new file mode 100644 index 0000000..2b2dc2a Binary files /dev/null and b/weather_test.db differ