Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Sunshine And March Vibes (2025 Wallpapers Edition)

      May 16, 2025

      The Case For Minimal WordPress Setups: A Contrarian View On Theme Frameworks

      May 16, 2025

      How To Fix Largest Contentful Paint Issues With Subpart Analysis

      May 16, 2025

      How To Prevent WordPress SQL Injection Attacks

      May 16, 2025

      Microsoft has closed its “Experience Center” store in Sydney, Australia — as it ramps up a continued digital growth campaign

      May 16, 2025

      Bing Search APIs to be “decommissioned completely” as Microsoft urges developers to use its Azure agentic AI alternative

      May 16, 2025

      Microsoft might kill the Surface Laptop Studio as production is quietly halted

      May 16, 2025

      Minecraft licensing robbed us of this controversial NFL schedule release video

      May 16, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      The power of generators

      May 16, 2025
      Recent

      The power of generators

      May 16, 2025

      Simplify Factory Associations with Laravel’s UseFactory Attribute

      May 16, 2025

      This Week in Laravel: React Native, PhpStorm Junie, and more

      May 16, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      Microsoft has closed its “Experience Center” store in Sydney, Australia — as it ramps up a continued digital growth campaign

      May 16, 2025
      Recent

      Microsoft has closed its “Experience Center” store in Sydney, Australia — as it ramps up a continued digital growth campaign

      May 16, 2025

      Bing Search APIs to be “decommissioned completely” as Microsoft urges developers to use its Azure agentic AI alternative

      May 16, 2025

      Microsoft might kill the Surface Laptop Studio as production is quietly halted

      May 16, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»How to Fuzz Test Golang HTTP Services

    How to Fuzz Test Golang HTTP Services

    November 4, 2024

    As a developer, you can’t always envision all of the possible inputs your programs or functions might receive.

    Even though you can define the major edge cases, you still can’t predict how your program will behave in the case of some weird unexpected input. In other words, you can typically only find bugs you expect to find.

    That’s where fuzz testing or fuzzing comes to the rescue. And in this tutorial, you’ll learn how to perform fuzz testing in Go.

    Table of Contents

    • What is Fuzz Testing?

    • Fuzz Testing in Go

    • Fuzzing HTTP Services

    • Conclusion

    • Resources

    What is Fuzz Testing?

    Fuzzing is an automated software testing technique that involves inputting a large amount of valid, nearly-valid, or invalid random data into a computer program and observing its behavior and output. So the goal of fuzzing is to reveal bugs, crashes, and security vulnerabilities in source code you might not find through traditional testing methods.

    The Fuzz Testing in Go video which I made a few years ago shows a very simple example of the Go code that may work well unless you provide a certain input:

    func Equal(a []byte, b []byte) bool {
      for i := range a {
        // can panic with runtime error: index out of range.
        if a[i] != b[i] {
          return false
        }
      }
    
      return true
    }
    

    This sample function works perfectly as long as the length of two slices are equal. But it will panic when the first slice is longer than the second (index out of range error). Furthermore, it doesn’t return a correct result when the second slice is the subset of the first one.

    The fuzzing technique would easily spot this bug by bombarding this function with various inputs.

    It’s a good practice to integrate fuzzing into your team’s software development lifecycle (SDLC) as well. For example, Microsoft uses fuzzing as one of the stages in its SDLC, to find potential bugs and vulnerabilities.

    Fuzz Testing in Go

    There are many fuzzing tools that have been available for a while – such as oss-fuzz, for example – but since Go 1.18, fuzzing was added to Go’s standard library. So it’s now part of the regular testing package since it’s a kind of test. You can also use it together with the other testing primitives which is nice.

    The steps to create a fuzz test in Go are the following:

    1. In a _test.go file, create a function that starts with Fuzz which accepts *testing.F

    2. Add corpus seeds using f.Add() to allow the fuzzer to generate the data based on it.

    3. Call the fuzz target using f.Fuzz() by passing fuzzing arguments which our target function accepts.

    4. Start the fuzzer using the regular go test command, but with the –fuzz=Fuzz flag

    Note that the fuzzing arguments can only be the following types:

    • string, byte, []byte

    • int, int8, int16, int32/rune, int64

    • uint, uint8, uint16, uint32, uint64

    • float32, float64

    • bool

    A simple fuzz test for the Equal function above may look like this:

    // Fuzz test
    func FuzzEqual(f *testing.F) {
      // Seed corpus addition
      f.Add([]byte{'f', 'u', 'z', 'z'}, []byte{'t', 'e', 's', 't'})
    
      // Fuzz target with fuzzing arguments
      f.Fuzz(func(t *testing.T, a []byte, b []byte) {
        // Call our target function and pass fuzzing arguments
        Equal(a, b)
      })
    }
    

    By default, fuzz tests run forever, so you either need to specify the time limit or wait for fuzz tests to fail. You can specify which tests to run using the --fuzz argument.

    go test --fuzz=Fuzz -fuzztime=10s
    

    If there are any errors during the execution, the output should look similar to this:

    go test --fuzz=Fuzz -fuzztime=30s
    --- FAIL: FuzzEqual (0.02s)
        --- FAIL: FuzzEqual (0.00s)
            testing.go:1591: panic: runtime error: index out of range
        Failing input written to testdata/fuzz/FuzzEqual/84ed65595ad05a58
        To re-run:
        go test -run=FuzzEqual/84ed65595ad05a58
    

    Notice that the input for which the fuzz test has failed are written into a file in the testdata folder and can be re-played by using that input identifier.

    go test -run=FuzzEqual/84ed65595ad05a58
    

    The testdata folder can be checked into the repository and be used for regular tests, because fuzz tests can also act as regular tests when executed without the --fuzz flag.

    Fuzzing HTTP Services

    It’s also possible to fuzz test the HTTP services by writing a test for your HandlerFunc and using the httptest package. This can be very useful if you need to test the whole HTTP service, not only the underlying functions.

    Let’s now introduce a more real example such as an HTTP Handler that accepts some user input in the request body and then write a fuzz test for it.

    Our handler accepts a JSON request with limit and offset fields to paginate some static mocked data. Let’s define the types first.

    type Request struct {
      Limit  int `json:"limit"`
      Offset int `json:"offset"`
    }
    
    type Response struct {
      Results    []int `json:"items"`
      PagesCount int   `json:"pagesCount"`
    }
    

    Our handler function then parses the JSON, paginates the static slice, and returns a new JSON in response.

    func ProcessRequest(w http.ResponseWriter, r *http.Request) {
     var req Request
    
      // Decode JSON request
      if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
      }
    
     // Apply offset and limit to some static data
     all := make([]int, 1000)
     start := req.Offset
     end := req.Offset + req.Limit
     res := Response{
       Results:    all[start:end],
       PagesCount: len(all) / req.Limit,
     }
    
     // Send JSON response
     if err := json.NewEncoder(w).Encode(res); err != nil {
       http.Error(w, err.Error(), http.StatusInternalServerError)
       return
     }
    
     w.WriteHeader(http.StatusOK)
    }
    

    As you may have already noticed, this function doesn’t handle slice operations very well and can easily panic. Also, it can panic if it tries to divide by 0. It’s great if we can spot this during the development or using only unit tests, but sometimes not everything is visible to our eye, and our handler may pass the input to other functions and so forth.

    Following our FuzzEqual example above, let’s implement a fuzz test for the ProcessRequest handler. The first thing we need to do is to provide the sample inputs for the fuzzer. This is the data that the fuzzer will use and modify into new inputs that are tried. We can craft some sample JSON request and use f.Add() with the []byte type.

    func FuzzProcessRequest(f *testing.F) {
      // Create sample inputs for the fuzzer
      testRequests := []Request{
        {Limit: -10, Offset: -10},
        {Limit: 0, Offset: 0},
        {Limit: 100, Offset: 100},
        {Limit: 200, Offset: 200},
      }
    
      // Add to the seed corpus
      for _, r := range testRequests {
        if data, err := json.Marshal(r); err == nil {
          f.Add(data)
        }
      }
    
      // ...
    }
    

    After that we can use the httptest package to create a test HTTP server and make requests to it.

    Note: Since our fuzzer can generate invalid non-JSON requests, it’s better just to skip them and ignore with t.Skip(). We can also skip BadRequest errors.

    func FuzzProcessRequest(f *testing.F) {
      // ...
      // Create a test server
      srv := httptest.NewServer(http.HandlerFunc(ProcessRequest))
      defer srv.Close()
    
      // Fuzz target with a single []byte argument
      f.Fuzz(func(t *testing.T, data []byte) {
        var req Request
        if err := json.Unmarshal(data, &req); err != nil {
          // Skip invalid JSON requests that may be generated during fuzz
          t.Skip("invalid json")
        }
    
        // Pass data to the server
        resp, err := http.DefaultClient.Post(srv.URL, "application/json", bytes.NewBuffer(data))
        if err != nil {
          t.Fatalf("unable to call server: %v, data: %s", err, string(data))
        }
        defer resp.Body.Close()
    
        // Skip BadRequest errors
        if resp.StatusCode == http.StatusBadRequest {
          t.Skip("invalid json")
        }
    
        // Check status code
        if resp.StatusCode != http.StatusOK {
          t.Fatalf("non-200 status code %d", resp.StatusCode)
        }
      })
    }
    

    Our fuzz target has a single argument with a type []byte that contains the full JSON request, but you can change it to have multiple arguments.

    Everything is ready now to run our fuzz tests. When fuzzing HTTP servers, you may need to adjust the amount of parallel workers, otherwise the load may overwhelm the test server. You can do that by setting -parallel=1 flag.

    go test --fuzz=Fuzz -fuzztime=10s -parallel=1
    go test --fuzz=Fuzz -fuzztime=30s
    --- FAIL: FuzzProcessRequest (0.02s)
        --- FAIL: FuzzProcessRequest (0.00s)
            runtime error: integer divide by zero
            runtime error: slice bounds out of range
    

    And as expected, we will see the above errors uncovered.

    We can also see the fuzz inputs in the testdata folder to see which JSON contributed to this failure. Here is a sample content of the file:

    go test fuzz v1
    []byte("{"limit":0,"offset":0}")
    

    To fix that issue, we can introduce input validation and default settings:

    if req.Limit <= 0 {
      req.Limit = 1
    }
    if req.Offset < 0 {
      req.Offset = 0
    }
    if req.Offset > len(all) {
      start = len(all) - 1
    }
    if end > len(all) {
      end = len(all)
    }
    

    With this change, the fuzz tests will run for 10 seconds and exit without an error.

    Conclusion

    Writing fuzz tests for your HTTP services or any other methods is a great way to detect hard-to-find bugs. Fuzzers can detect hard-to-spot bugs that happen for only some weird unexpected input.

    It’s amazing to see that fuzzing is a part of Go’s built-in testing library, making it easy to combine with regular tests. Note: prior to Go 1.18, developers used go-fuzz, which is a great tool for fuzzing as well.

    Resources

    • Source Code

    • Fuzz Testing in Go

    • Go Fuzzing

    Source: freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More 

    Facebook Twitter Reddit Email Copy Link
    Previous Article15 Ways to Earn from Home
    Next Article Become an Open Source Master

    Related Posts

    Security

    Nmap 7.96 Launches with Lightning-Fast DNS and 612 Scripts

    May 16, 2025
    Common Vulnerabilities and Exposures (CVEs)

    CVE-2025-2305 – Apache Linux Path Traversal Vulnerability

    May 16, 2025
    Leave A Reply Cancel Reply

    Continue Reading

    AI in Logo Design: Should Designers Be Worried?

    Web Development

    Consumer Behavior: The Catalyst for Digital Innovation

    Development

    FinTextQA: A Long-Form Question Answering LFQA Dataset Specifically Designed for the Financial Domain

    Development

    5 Linux commands for better group management (and how to use them)

    Development

    Highlights

    Development

    This AI Paper Proposes Utilizing the AI-Based Agents Workflow (AgWf) Paradigm to Enhance the Effectiveness of Process Mining (PM) on LLMs

    August 21, 2024

    Process mining is a part of data science concerned with analyzing event logs produced by…

    Finally, a wireless microphone that effectively replaces my Shure shotgun when traveling

    April 24, 2025

    Cyberespionage Threat: APT-C-60 Targets East Asia with SpyGlace

    August 30, 2024

    Power Checklist: Managing Backups

    February 18, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.