Tutorial ini memperkenalkan dasar-dasar dari fuzzing pada Go. Dengan fuzzing, data acak dijalankan pada tes dengan tujuan menemukan celah keamanan atau input yang menyebabkan program berhenti. Beberapa contoh celah keamanan yang dapat ditemukan oleh fuzzing yaitu serangan injeksi SQL, buffer overflow, denial of service, dan cross-site scripting.

Dalam tutorial ini, kita akan menulis sebuah pengujian fuzz untuk sebuah fungsi sederhana, mempelajari perintah-perintah go test untuk fuzzing, dan mencari dan memperbaiki isu-isu yang ditemukan fuzz tes.

Untuk informasi lebih lanjut tentang terminologi dalam tutorial ini, lihatlah glosarium Go Fuzzing.

Kita akan mempelajari beberapa bagian-bagian berikut,

Note
Untuk tutorial lainnya, lihat Tutorial.
Note
Saat ini fuzzing pada Go hanya mendukung sebagian dari tipe-tipe bawaan, yang terdaftar di dokumentasi Go Fuzzing, dukungan tipe bawaan lainnya akan ditambahkan di masa depan.

Kebutuhan

  • Pemasangan Go 1.18 atau versi terbaru. Untuk instruksi pemasangan, lihat Memasang Go.

  • Perkakas untuk menyunting kode.

  • Terminal perintah. Go bekerja pada terminal apa pun di Linux dan Mac, dan PowerShell atau cmd pada Windows.

  • Sistem yang mendukung fuzzing. Fuzzing pada Go dengan instrumentasi cakupan saat ini hanya tersedia pada arsitektur AMD64 dan ARM64.

Membuat direktori penyimpanan kode

Untuk memulai, buatlah sebuah direktori tempat berkas kode akan disimpan,

  1. Buka terminal dan pindah ke direktori pengguna Anda,

    Pada Linux atau Mac:

    $ cd

    Pada Windows:

    C:\> cd %HOMEPATH%

    Selanjutnya pada tutorial ini, penulisan $ ditandai sebagai prompt pada terminal. Perintah yang digunakan pada prompt tersebut bisa berjalan di Windows.

  2. Dari terminal, buat sebuah direktori untuk kode Anda yang diberi nama fuzz.

    $ mkdir fuzz
    $ cd fuzz
  3. Buatlah sebuah Go modul untuk menyimpan kode Anda.

    Jalankan perintah go mod init, dengan menambahkan path ke modul kode Anda.

    $ go mod init example/fuzz
    go: creating new go.mod: module example/fuzz
    Note
    Untuk kode tingkat lanjut, Anda sebaiknya menamakan path modul yang lebih spesifik sesuai kebutuhan Anda. Untuk informasi lebih lanjut lihat Manajemen dependensi.

Selanjutnya, kita akan menambahkan fungsi sederhana untuk membalikan sebuah string, yang akan kita fuzz tes nantinya.

Menambahkan kode yang akan dites

Pada langkah ini, kita akan membuat sebuah fungsi untuk membalikan sebuah string.

  1. Menggunakan perkakas sunting, buat sebuah berkas bernama main.go di dalam direktori fuzz.

  2. Di dalam main.go, salin lah deklarasi paket berikut

    package main

    Sebuah program (bukan sebuah pustaka) selalu dibuat dengan paket main.

  3. Di bawah deklarasi paket, salin deklarasi fungsi berikut.

    func Reverse(s string) string {
    	b := []byte(s)
    	for i, j := 0, len(b)-1; i < len(b)/2; i, j = i+1, j-1 {
    		b[i], b[j] = b[j], b[i]
    	}
    	return string(b)
    }

    Fungsi ini menerima sebuah string s, melakukan pengulangan per tiap byte dari string s, dan mengembalikan sebuah string yang terbalik.

    Note
    Kode ini berdasarkan fungsi stringutil.Reverse yang ada di dalam golang.org/x/example.
  4. Pada bagian selanjutnya, salin fungsi main berikut yang menginisialisasi sebuah string input, membalikkan isinya, mencetaknya, dan membalikkan isinya lagi.

    func main() {
    	input := "The quick brown fox jumped over the lazy dog"
    	rev := Reverse(input)
    	doubleRev := Reverse(rev)
    	fmt.Printf("original: %q\n", input)
    	fmt.Printf("reversed: %q\n", rev)
    	fmt.Printf("reversed again: %q\n", doubleRev)
    }

    Fungsi ini akan menjalankan operasi Reverse, mencetak hasilnya ke terminal. Hal ini berguna untuk melihat operasi dari kode dan juga untuk memeriksa hasilnya.

  5. Fungsi main menggunakan paket fmt, jadi kita harus mengimpornya.

    Baris-baris awal pada main.go menjadi seperti berikut,

    package main
    
    import "fmt"

Jalankan kode

Dari terminal, masih di dalam direktori yang berisi main.go, jalankan kode dengan cara berikut

$ go run .
original: "The quick brown fox jumped over the lazy dog"
reversed: "god yzal eht revo depmuj xof nworb kciuq ehT"
reversed again: "The quick brown fox jumped over the lazy dog"

Kita dapat melihat string asli, hasil dari pembalikan, dan hasil dari pembalikan kedua kalinya, yang seharusnya sama dengan yang asli.

Setelah kode berjalan, saatnya untuk membuat unit tes.

Menambahkan unit tes

Pada langkah ini, kita akan menulis sebuah unit tes sederhana untuk fungsi Reverse.

  1. Menggunakan perkakas sunting, buat sebuah berkas bernama reverse_test.go di dalam direktori fuzz.

  2. Salin kode berikut ke dalam berkas reverse_test.go.

    package main
    
    import (
    	"testing"
    )
    
    func TestReverse(t *testing.T) {
    	testcases := []struct {
    		in, want string
    	}{
    		{"Hello, world", "dlrow ,olleH"},
    		{" ", " "},
    		{"!12345", "54321!"},
    	}
    	for _, tc := range testcases {
    		rev := Reverse(tc.in)
    		if rev != tc.want {
    			t.Errorf("Reverse: %q, want %q", rev, tc.want)
    		}
    	}
    }

    Kode tes tersebut akan memeriksa bahwa daftar string input akan secara benar terbalik.

Jalankan tes

Jalankan unit tes menggunakan go test

$ go test
PASS
ok      example/fuzz  0.013s

Selanjutnya, kita akan mengubah unit tes menjadi sebuah fuzz tes.

Menambahkan fuzz tes

Unit tes memiliki batasan, setiap input harus ditambahkan secara manual ke dalam tes kode. Salah satu kelebihan dari fuzzing yaitu ia menemukan dan menambahkan input ke dalam tes kode secara otomatis, dan bisa menemukan kasus-kasus khusus yang mana tidak terpikirkan pada unit tes biasa.

Pada bagian ini kita akan mengonversi unit tes menjadi fuzz tes supaya kita dapat memiliki input pengujian lebih banyak dengan sedikit bekerja!

Ingatlah bahwa kita bisa menyimpan kode unit tes, benchmark, dan fuzz tes di dalam berkas *_test.go yang sama, namun untuk contoh ini kita akan mengonversi unit tes menjadi fuzz tes.

Pada perkakas sunting Anda, timpa unit tes di dalam reverse_test.go dengan fuzz tes berikut.

func FuzzReverse(f *testing.F) {
	testcases := []string{"Hello, world", " ", "!12345"}
	for _, tc := range testcases {
		f.Add(tc)  // Penambahan input ke bibit corpus.
	}
	f.Fuzz(func(t *testing.T, orig string) {
		rev := Reverse(orig)
		doubleRev := Reverse(rev)
		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)
		}
	})
}

Fuzzing juga memiliki beberapa batasan. Pada unit tes, kita bisa memprediksi ekspektasi keluaran dari fungsi Reverse, dan memverifikasi bahwa keluaran sama dengan ekspektasi.

Contohnya, pada kasus Reverse("Hello, world"), kita dapat menentukan ekspektasi keluaran sebagai "dlrow ,olleH".

Pada fuzzing, kita tidak dapat memprediksi ekspektasi keluaran, secara kita tidak punya kontrol terhadap input.

Namun, ada beberapa properti dari fungsi Reverse yang dapat kita verifikasi dalam fuzz tes. Dua properti yang kita cek dalam fuzz tes adalah:

  1. Membalikan sebuah string dua kali akan mengeluarkan string aslinya.

  2. String yang terbalik adalah UTF-8 yang valid.

Perhatikan perbedaan sintaksis antara unit tes dan fuzz tes:

  • Fungsi dimulai dengan FuzzXxx bukan TestXxx, dan menerima *testing.F bukan *testing.T.

  • Di unit tes kita menggunakan t.Run, di fuzz tes kita menggunakan f.Fuzz yang menerima sebuah fungsi target dengan parameter *testing.T dan tipe-tipe yang akan di-fuzz. Input-input dari unit tes diberikan sebagai bibit corpus menggunakan f.Add.

Selanjutnya, pastikan paket unicode/utf8 telah diimpor.

package main

import (
	"testing"
	"unicode/utf8"
)

Dengan unit tes yang telah dikonversi menjadi fuzz tes, saatnya kita jalankan kembali pengujian.

Menjalankan tes

  1. Jalankan fuzz tes tanpa opsi fuzzing untuk memastikan bibit input benar.

    $ go test
    PASS
    ok      example/fuzz  0.013s

    Kita juga dapat menjalankan dengan cara go test -run=Fuzz jika ada unit tes lain di dalam berkas namun kita ingin hanya menjalankan fuzz tes.

  2. Jalankan FuzzReverse dengan fuzzing, untuk melihat apakah string input yang dihasilkan secara acak akan menyebabkan kegagalan.

    Caranya yaitu dengan mengeksekusi go test dengan opsi -fuzz dan parameter Fuzz. Salin perintah berikut ke terminal,

    $ go test -fuzz=Fuzz

    Opsi fuzzing lain yang berguna yaitu -fuzztime, yang membatasi waktu berjalannya sebuah fuzz tes. Contohnya, menggunakan opsi -fuzztime 10s berarti selama tidak ada kesalahan terjadi, fuzz tes akan berhenti setelah 10 detik. Lihat bagian opsi testing dari dokumentasi "cmd/go" untuk mengetahui lebih lanjut tentang opsi-opsi pengujian.

    Sekarang jalankan perintah tersebut,

    $ go test -fuzz=Fuzz
    fuzz: elapsed: 0s, gathering baseline coverage: 0/3 completed
    fuzz: elapsed: 0s, gathering baseline coverage: 3/3 completed, now fuzzing with 8 workers
    fuzz: minimizing 38-byte failing input file...
    --- FAIL: FuzzReverse (0.01s)
        --- FAIL: FuzzReverse (0.00s)
            reverse_test.go:20: Reverse produced invalid UTF-8 string "\x9c\xdd"
    
        Failing input written to testdata/fuzz/FuzzReverse/af69258a12129d6c
        To re-run:
        go test -run=FuzzReverse/af69258a12129d6c
    FAIL
    exit status 1
    FAIL    example/fuzz  0.030s

    Sebuah kegagalan terjadi selama fuzzing, dan input yang menyebabkan kegagalan tersebut ditulis ke dalam berkas bibit corpus yang akan dijalankan kembali saat go test dieksekusi, walaupun tanpa opsi -fuzz. Untuk melihat input yang menyebabkan kegagalan, bukalah berkas yang ada di dalam direktori "testdata/fuzz/FuzzReverse/". Berkas bibit corpus Anda bisa jadi berbeda, namun formatnya akan sama.

    go test fuzz v1
    string("泃")

    Baris pertama dari berkas corpus mengindikasikan versi fuzz tes. Baris selanjutnya merepresentasikan nilai dari setiap tipe yang digunakan sebagai parameter saat mem-fuzzing fungsi target FuzzReverse. Secara fungsi target hanya menerima 1 input, maka hanya ada 1 nilai.

  3. Jalankan lagi go test tanpa opsi -fuzz; maka isi di dalam bibit corpus akan secara otomatis digunakan:

    $ go test
    --- FAIL: FuzzReverse (0.00s)
        --- FAIL: FuzzReverse/af69258a12129d6c (0.00s)
            reverse_test.go:20: Reverse produced invalid UTF-8 string "\x9c\xdd"
    FAIL
    exit status 1
    FAIL    example/fuzz  0.016s

    Secara tes kita sudah gagal, saatnya melakukan pemeriksaan dan memperbaiki kode.

Memperbaiki kesalahan karena string tidak valid

Pada bagian ini, kita akan memeriksa penyebab dari kesalahan string tidak valid dan memperbaikinya.

Anda bisa mencoba memeriksa dan memperbaikinya sendiri terlebih dahulu sebelum lanjut ke bagian selanjutnya.

Diagnosis kesalahan

Ada beberapa cara untuk mendiagnosis kesalahan pada kode. Jika Anda menggunakan perkakas VS Code, Anda dapat mengatur debugger untuk menginvestigasinya.

Pada tutorial ini, kita akan memeriksa kode dengan menulis hasil ke terminal.

Pertama, perhatikan dokumentasi dari utf8.ValidString.

ValidString reports whether s consists entirely of valid UTF-8-encoded runes.

Fungsi Reverse yang sekarang membalikan string secara per-byte, dan disanalah letak kesalahan kita. Untuk menjaga string tetap valid pada UTF-8, kita harus membalikan string per-rune.

Untuk memeriksa kenapa input (pada kasus ini, karakter China ) menyebabkan fungsi Reverse menghasilkan string tidak valid saat dibalik, kita dapat memeriksa jumlah rune di dalam string yang telah dibalik.

Dalam perkakas sunting Anda, timpa target fuzz FuzzReverse dengan kode berikut.

f.Fuzz(func(t *testing.T, orig string) {
	rev := Reverse(orig)
	doubleRev := Reverse(rev)
	t.Logf("Number of runes: orig=%d, rev=%d, doubleRev=%d",
		utf8.RuneCountInString(orig), utf8.RuneCountInString(rev),
		utf8.RuneCountInString(doubleRev))
	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)
	}
})

Baris t.Logf akan mencetak ke terminal bila kesalahan terjadi, atau bila menjalankan tes dengan opsi -v, yang dapat membantu mendiagnosis kesalahan.

Menjalankan tes

Jalankan tes dengan go test

$ go test
--- FAIL: FuzzReverse (0.00s)
    --- FAIL: FuzzReverse/af69258a12129d6c (0.00s)
        reverse_test.go:16: Number of runes: orig=1, rev=3, doubleRev=1
        reverse_test.go:21: Reverse produced invalid UTF-8 string "\x83\xb3\xe6"
FAIL
exit status 1
FAIL    example/fuzz    0.598s

Seluruh bibit corpus berisi string yang mana setiap karakter adalah sebuah byte, sehingga apabila dibalik hasilnya sesuai dengan yang kita bayangkan. Namun, karakter membutuhkan beberapa byte. Maka, membalikannya satu per satu per-byte akan menyebabkan string tidak valid.

Note
Jika Anda penasaran bagaimana Go memperlakukan string, bacalah blog String, byte, rune, dan karakter dalam Go untuk memahami lebih lanjut.

Dengan lebih memahami kesalahan yang terjadi, mari kita dapat memperbaiki fungsi Reverse.

Memperbaiki kesalahan

Untuk memperbaiki fungsi Reverse, kita lakukan pengulangan pada string dengan cara per-rune bukan per-byte.

Pada perkakas sunting, timpa fungsi Reverse dengan kode berikut.

func Reverse(s string) string {
	r := []rune(s)
	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}

Kunci perbedaannya yaitu fungsi Reverse yang baru diiterasi untuk setiap rune pada string, bukan setiap byte. Ingatlah bahwa ini hanyalah contoh saja, fungsi ini tidak menangani kasus karakter kombinasi secara benar.

Jalankan kode

  1. Jalankan tes menggunakan go test

    $ go test
    PASS
    ok      example/fuzz  0.016s

    Tes sekarang berjalan dengan benar!

  2. Jalankan fuzz tes kembali dengan go test -fuzz, untuk melihat apakah ada kecacatan baru.

    $ go test -fuzz=Fuzz
    fuzz: elapsed: 0s, gathering baseline coverage: 0/10 completed
    fuzz: minimizing 49-byte failing input file
    fuzz: elapsed: 0s, gathering baseline coverage: 6/10 completed
    --- FAIL: FuzzReverse (0.00s)
        --- FAIL: FuzzReverse (0.00s)
            reverse_test.go:16: Number of runes: orig=1, rev=1, doubleRev=1
            reverse_test.go:20: Before: "\x8d", after: "�"
    
        Failing input written to testdata/fuzz/FuzzReverse/4e4e4dc480dc461c
        To re-run:
        go test -run=FuzzReverse/4e4e4dc480dc461c
    FAIL
    exit status 1
    FAIL    example/fuzz    0.005s

    Kita dapat lihat bahwa hasil string terakhir berbeda dari yang aslinya setelah dibalikkan dua kali. Kecacatan ini disebabkan oleh input dengan unicode tidak valid. Bagaimana ini bisa terjadi?

    Mari kita periksa kembali.

Memperbaiki kesalahan pada pembalikan dua kali

Pada bagian ini, kita akan memerikan kesalahan karena pembalikan dua kali dan memperbaikinya.

Anda bisa memeriksa dan memperbaikinya sendiri terlebih dahulu sebelum lanjut ke bagian selanjutnya.

Diagnosis kesalahan

Seperti sebelumnya, ada beberapa cara untuk mendiagnosis kesalahan ini. Pada kasus ini, menggunakan debugger adalah pendekatan yang lebih baik.

Dalam tutorial ini, kita akan mendiagnosis dengan menulis informasi yang diperlukan ke terminal.

Perhatikan secara seksama pada hasil string terbalik yang pertama untuk menemukan kesalahan yang terjadi. Pada Go, sebuah string adalah rangkaian byte-byte read-only dan dapat berisi byte yang tidak valid dalam UTF-8. String input berisi satu byte, "\x8d". Saat input string dikonversi menjadi []rune, Go mengubah rangkaian byte menjadi UTF-8, dan menimpa byte "\8d" menjadi karakter "�". Saat kita membandingkan karakter UTF-8 yang telah dibalikan lagi dengan rangkaian input byte, mereka tidak akan sama.

Pada perkakas sunting Anda, timpa fungsi Reverse dengan kode berikut,

func Reverse(s string) string {
	fmt.Printf("input: %q\n", s)
	r := []rune(s)
	fmt.Printf("runes: %q\n", r)
	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}

Hal ini akan membantu kita memahami apa yang menyebabkan kesalahan saat mengonversi string ke slice rune.

Menjalankan kode

Kali ini, kita hanya ingin menjalankan tes yang gagal untuk memeriksa log. Untuk itu, kita akan menggunakan go test -run.

Untuk menjalankan isi dari corpus tertentu dalam testdata/FuzzXxx, kita dapat mengirim opsi {FuzzTestName}/{nama berkas} pada opsi -run. Hal ini sangat berguna saat melakukan diagnosis. Salin nilai hash dari terminal pada hasil tes sebelumnya pada opsi -run (nilai hash bisa berbeda dengan yang tertulis di bawah),

$ go test -run=FuzzReverse/4e4e4dc480dc461c
input: "\x8d"
runes: ['�']
input: "�"
runes: ['�']
--- FAIL: FuzzReverse (0.00s)
    --- FAIL: FuzzReverse/4e4e4dc480dc461c (0.00s)
        reverse_test.go:16: Number of runes: orig=1, rev=1, doubleRev=1
        reverse_test.go:20: Before: "\x8d", after: "�"
FAIL
exit status 1
FAIL    example/fuzz    0.001s

Setelah mengetahui bahwa input adalah unicode yang tidak valid, mari kita perbaiki kesalahan pada fungsi Reverse kita.

Memperbaiki kesalahan

Untuk memperbaiki kesalahan, kita kembalikan sebuah error bila input dari fungsi Reverse bukanlah UTF-8 yang valid.

  1. Pada perkakas sunting, timpa fungsi Reverse dengan kode berikut.

    func Reverse(s string) (string, error) {
    	if !utf8.ValidString(s) {
    		return s, errors.New("input is not valid UTF-8")
    	}
    	r := []rune(s)
    	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
    		r[i], r[j] = r[j], r[i]
    	}
    	return string(r), nil
    }

    Perubahan ini akan mengembalikan sebuah error bila string input berisi karakter UTF-8 yang tidak valid.

  2. Secara fungsi Reverse sekarang mengembalikan sebuah error, ubah fungsi main untuk mengabaikan nilai error. Timpa fungsi main dengan kode berikut.

    func main() {
    	input := "The quick brown fox jumped over the lazy dog"
    	rev, revErr := Reverse(input)
    	doubleRev, doubleRevErr := Reverse(rev)
    	fmt.Printf("original: %q\n", input)
    	fmt.Printf("reversed: %q, err: %v\n", rev, revErr)
    	fmt.Printf("reversed again: %q, err: %v\n", doubleRev, doubleRevErr)
    }

    Pemanggilan Reverse seharusnya mengembalikan nilai error nil, secara string input adalah UTF-8 yang valid.

  3. Kita impor paket errors dan unicode/utf8.

    Perintah impor pada main.go berbentuk seperti berikut.

    import (
    	"errors"
    	"fmt"
    	"unicode/utf8"
    )
  4. Ubah berkas reverse_test.go untuk memeriksa error dan mengabaikan tes bila error dikembalikan tidak 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 {
    			return
    		}
    		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)
    		}
    	})
    }

    Selain dengan menggunakan return, kita juga dapat memanggil t.Skip() untuk menghentikan eksekusi dari input fuzz.

Menjalankan kode

  1. Jalankan tes dengan go test

    $ go test
    PASS
    ok      example/fuzz  0.019s
  2. Jalankan fuzz tes dengan go test -fuzz=Fuzz, kemudian setelah beberapa detik, hentikan fuzzing dengan menekan CTRL-C.

    Fuzz tes akan terus berjalan sampai ia menemukan input yang menyebabkan kegagalan kecuali kita menambahkan opsi -fuzztime. Secara bawaan ia akan berjalan terus jika tidak ada kesalahan ditemukan, namun proses tersebut dapat dihentikan paksa dengan CTRL-C.

    $ go test -fuzz=Fuzz
    fuzz: elapsed: 0s, gathering baseline coverage: 0/38 completed
    fuzz: elapsed: 0s, gathering baseline coverage: 38/38 completed, now fuzzing with 4 workers
    fuzz: elapsed: 3s, execs: 86342 (28778/sec), new interesting: 2 (total: 35)
    fuzz: elapsed: 6s, execs: 193490 (35714/sec), new interesting: 4 (total: 37)
    fuzz: elapsed: 9s, execs: 304390 (36961/sec), new interesting: 4 (total: 37)
    ...
    fuzz: elapsed: 3m45s, execs: 7246222 (32357/sec), new interesting: 8 (total: 41)
    ^Cfuzz: elapsed: 3m48s, execs: 7335316 (31648/sec), new interesting: 8 (total: 41)
    PASS
    ok      example/fuzz  228.000s
  3. Jalankan fuzz dengan go test -fuzz=Fuzz -fuzztime 30s yang akan berhenti setelah 30 detik bila tidak ditemukan kesalahan.

    $ go test -fuzz=Fuzz -fuzztime 30s
    fuzz: elapsed: 0s, gathering baseline coverage: 0/5 completed
    fuzz: elapsed: 0s, gathering baseline coverage: 5/5 completed, now fuzzing with 4 workers
    fuzz: elapsed: 3s, execs: 80290 (26763/sec), new interesting: 12 (total: 12)
    fuzz: elapsed: 6s, execs: 210803 (43501/sec), new interesting: 14 (total: 14)
    fuzz: elapsed: 9s, execs: 292882 (27360/sec), new interesting: 14 (total: 14)
    fuzz: elapsed: 12s, execs: 371872 (26329/sec), new interesting: 14 (total: 14)
    fuzz: elapsed: 15s, execs: 517169 (48433/sec), new interesting: 15 (total: 15)
    fuzz: elapsed: 18s, execs: 663276 (48699/sec), new interesting: 15 (total: 15)
    fuzz: elapsed: 21s, execs: 771698 (36143/sec), new interesting: 15 (total: 15)
    fuzz: elapsed: 24s, execs: 924768 (50990/sec), new interesting: 16 (total: 16)
    fuzz: elapsed: 27s, execs: 1082025 (52427/sec), new interesting: 17 (total: 17)
    fuzz: elapsed: 30s, execs: 1172817 (30281/sec), new interesting: 17 (total: 17)
    fuzz: elapsed: 31s, execs: 1172817 (0/sec), new interesting: 17 (total: 17)
    PASS
    ok      example/fuzz  31.025s

    Pengujian fuzz sukses!

    Sebagai tambahan dari opsi -fuzz, ada beberapa opsi baru yang telah ditambahkan ke go test yang dapat dilihat di dokumentasi.

    Lihat Go Fuzzing untuk informasi lebih lanjut tentang istilah-istilah pada keluaran fuzzing. Misalnya, istilah "new interesting" mengacu pada input-input baru yang telah ditambahkan sesuai cakupan kode dari corpus fuzz tes yang telah ada. Jumlah input dari "new interesting" bisa meningkat saat fuzzing dimulai, terus naik beberapa kali saat menjelajahi baris kode yang baru, kemudian berkurang seiring waktu.

Kesimpulan

Selamat! Anda baru saja berkenalan dengan fuzzing pada Go.

Langkah selanjutnya yaitu memilih sebuah fungsi pada kode Anda yang dapat dites dengan fuzzing, dan mencobanya! Jika fuzzing menemukan kecacatan dalam kode Anda, tambahkan proyek Anda ke lemari piala pada wiki Go.

Jika Anda mengalami kendala atau memiliki sebuah ide untuk fitur baru, silahkan buat isu baru.

Untuk diskusi dan umpan balik tentang fitur, Anda dapat berpartisipasi dalam kanal #fuzzing di Slack Gophers.

Lihatlah dokumentasi tentang Go Fuzzing untuk bacaan lebih lanjut.

Seluruh kode

Berkas main.go,

package main

import (
	"errors"
	"fmt"
	"unicode/utf8"
)

func Reverse(s string) (string, error) {
	if !utf8.ValidString(s) {
		return s, errors.New("input is not valid UTF-8")
	}
	r := []rune(s)
	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r), nil
}

func main() {
	input := "The quick brown fox jumped over the lazy dog"
	rev, revErr := Reverse(input)
	doubleRev, doubleRevErr := Reverse(rev)
	fmt.Printf("original: %q\n", input)
	fmt.Printf("reversed: %q, err: %v\n", rev, revErr)
	fmt.Printf("reversed again: %q, err: %v\n", doubleRev, doubleRevErr)
}

Berkas reverse_test.go,

package main

import (
	"testing"
	"unicode/utf8"
)

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 {
			return
		}
		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)
		}
	})
}