Skip to content
This repository was archived by the owner on Apr 30, 2021. It is now read-only.

Commit c406433

Browse files
authored
Merge pull request #22 from fuzzitdev/local_job
Add Local Sanity option via docker
2 parents 47ba2a2 + c5a71ec commit c406433

File tree

14 files changed

+461
-60
lines changed

14 files changed

+461
-60
lines changed

client/auth.go

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"io/ioutil"
1010
"log"
1111
"net/url"
12-
"os"
1312
"os/user"
1413
"path"
1514
"time"
@@ -19,33 +18,7 @@ import (
1918
"google.golang.org/api/option"
2019
)
2120

22-
func (c *fuzzitClient) ReAuthenticate(force bool) error {
23-
usr, err := user.Current()
24-
if err != nil {
25-
log.Fatal(err)
26-
}
27-
cacheFile := path.Join(usr.HomeDir, ".fuzzit.cache")
28-
29-
if !force {
30-
file, err := os.Open(cacheFile)
31-
if err != nil {
32-
return err
33-
}
34-
35-
err = json.NewDecoder(file).Decode(c)
36-
file.Close()
37-
if err != nil {
38-
// try to prevent being stuck forever if cache file gets corrupted
39-
os.Remove(cacheFile) // if a file
40-
os.RemoveAll(cacheFile) // if a directory
41-
return err
42-
}
43-
}
44-
45-
if c.ApiKey == "" {
46-
return errors.New("API Key is no configured, please run fuzzit auth [api_key]")
47-
}
48-
21+
func (c *fuzzitClient) refreshToken() error {
4922
if c.IdToken == "" || (time.Now().Unix()-c.LastRefresh) > 60*45 {
5023
createCustomTokenEndpoint := fmt.Sprintf("%s/createCustomToken?api_key=%s", FuzzitEndpoint, url.QueryEscape(c.ApiKey))
5124
r, err := c.httpClient.Get(createCustomTokenEndpoint)
@@ -54,7 +27,7 @@ func (c *fuzzitClient) ReAuthenticate(force bool) error {
5427
}
5528
defer r.Body.Close()
5629
if r.StatusCode != 200 {
57-
return errors.New("API Key is not valid")
30+
return errors.New("API Key is not valid. Try running fuzzit auth <API_KEY> again")
5831
}
5932

6033
err = json.NewDecoder(r.Body).Decode(c)
@@ -81,6 +54,12 @@ func (c *fuzzitClient) ReAuthenticate(force bool) error {
8154
if err != nil {
8255
return err
8356
}
57+
58+
usr, err := user.Current()
59+
if err != nil {
60+
log.Fatal(err)
61+
}
62+
cacheFile := path.Join(usr.HomeDir, ".fuzzit.cache")
8463
err = ioutil.WriteFile(cacheFile, cBytes, 0644)
8564
if err != nil {
8665
return err
@@ -97,7 +76,6 @@ func (c *fuzzitClient) ReAuthenticate(force bool) error {
9776
tokenSource := oauth2.StaticTokenSource(&token)
9877
ctx := context.Background()
9978

100-
// some known issue with go afaik
10179
firestoreClient, err := firestore.NewClient(ctx, "fuzzit-b5fbf", option.WithTokenSource(tokenSource))
10280
c.firestoreClient = firestoreClient
10381

client/client.go

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ package client
22

33
import (
44
"cloud.google.com/go/firestore"
5+
"encoding/json"
6+
"log"
57
"net/http"
8+
"os"
9+
"os/user"
10+
"path"
611
"time"
712
)
813

@@ -15,6 +20,7 @@ type Target struct {
1520
type Job struct {
1621
TargetId string `firestore:"target_id"`
1722
Args string `firestore:"args"`
23+
Local bool
1824
Type string `firestore:"type"`
1925
Host string `firestore:"host"`
2026
Revision string `firestore:"revision"`
@@ -49,21 +55,48 @@ type fuzzitClient struct {
4955
httpClient *http.Client
5056
}
5157

52-
func NewFuzzitClient(apiKey string) *fuzzitClient {
58+
func NewFuzzitClient(apiKey string) (*fuzzitClient, error) {
5359
c := &fuzzitClient{}
5460
c.httpClient = &http.Client{Timeout: 60 * time.Second}
5561
c.ApiKey = apiKey
56-
57-
return c
62+
err := c.refreshToken()
63+
if err != nil {
64+
return nil, err
65+
}
66+
return c, nil
5867
}
5968

6069
func LoadFuzzitFromCache() (*fuzzitClient, error) {
6170
c := &fuzzitClient{}
6271
c.httpClient = &http.Client{Timeout: 60 * time.Second}
63-
err := c.ReAuthenticate(false)
72+
73+
usr, err := user.Current()
74+
if err != nil {
75+
log.Fatal(err)
76+
}
77+
cacheFile := path.Join(usr.HomeDir, ".fuzzit.cache")
78+
79+
if _, err := os.Stat(cacheFile); os.IsNotExist(err) {
80+
return c, nil
81+
}
82+
83+
file, err := os.Open(cacheFile)
84+
if err != nil {
85+
return nil, err
86+
}
87+
88+
err = json.NewDecoder(file).Decode(c)
89+
file.Close()
6490
if err != nil {
91+
// try to prevent being stuck forever if cache file gets corrupted
92+
os.Remove(cacheFile) // if a file
93+
os.RemoveAll(cacheFile) // if a directory
6594
return nil, err
6695
}
6796

97+
//if c.ApiKey == "" {
98+
// return errors.New("API Key is not configured (will have access only to public repositories)")
99+
//}
100+
68101
return c, nil
69102
}

client/commands.go

Lines changed: 186 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,88 @@
11
package client
22

33
import (
4+
"cloud.google.com/go/firestore"
45
"context"
56
"encoding/json"
67
"fmt"
8+
"github.com/docker/docker/api/types"
9+
"github.com/docker/docker/api/types/container"
10+
"github.com/docker/docker/pkg/stdcopy"
11+
"github.com/google/uuid"
12+
"github.com/mholt/archiver"
13+
"google.golang.org/api/iterator"
14+
"io"
715
"io/ioutil"
816
"log"
917
"os"
1018
"path/filepath"
1119
"strings"
12-
13-
"cloud.google.com/go/firestore"
14-
"github.com/google/uuid"
15-
"github.com/mholt/archiver"
16-
"google.golang.org/api/iterator"
20+
//"github.com/docker/docker/api/types/container"
21+
"github.com/docker/docker/client"
22+
//"github.com/docker/docker/pkg/stdcopy"
1723
)
1824

25+
func (c *fuzzitClient) archiveFiles(files []string) (string, error) {
26+
fuzzerPath := files[0]
27+
filename := filepath.Base(fuzzerPath)
28+
if !strings.HasSuffix(filename, ".tar.gz") {
29+
tmpDir, err := ioutil.TempDir("", "fuzzit")
30+
if err != nil {
31+
return "", err
32+
}
33+
dstPath := filepath.Join(tmpDir, "fuzzer")
34+
_, err = copyFile(dstPath, fuzzerPath)
35+
if err != nil {
36+
return "", err
37+
}
38+
39+
prefix, err := uuid.NewRandom()
40+
if err != nil {
41+
return "", err
42+
}
43+
filesToArchive := append([]string{dstPath}, files[1:]...)
44+
45+
tmpfile := filepath.Join(os.TempDir(), prefix.String()+".tar.gz")
46+
z := archiver.NewTarGz()
47+
err = z.Archive(filesToArchive, tmpfile)
48+
if err != nil {
49+
return "", err
50+
}
51+
fuzzerPath = tmpfile
52+
}
53+
54+
return fuzzerPath, nil
55+
}
56+
57+
func (c *fuzzitClient) DownloadSeed(dst string, target string) error {
58+
storagePath := fmt.Sprintf("orgs/%s/targets/%s/seed", c.Org, target)
59+
err := c.downloadFile(dst, storagePath)
60+
if err != nil {
61+
return err
62+
}
63+
return nil
64+
}
65+
66+
func (c *fuzzitClient) DownloadCorpus(dst string, target string) error {
67+
storagePath := fmt.Sprintf("orgs/%s/targets/%s/corpus", c.Org, target)
68+
err := c.downloadFile(dst, storagePath)
69+
if err != nil {
70+
return err
71+
}
72+
return nil
73+
}
74+
75+
func (c *fuzzitClient) DownloadFuzzer(dst string, target string, job string) error {
76+
storagePath := fmt.Sprintf("orgs/%s/targets/%s/jobs/%s/fuzzer", c.Org, target, job)
77+
err := c.downloadFile(dst, storagePath)
78+
if err != nil {
79+
return err
80+
}
81+
return nil
82+
}
83+
1984
func (c *fuzzitClient) GetResource(resource string) error {
20-
err := c.ReAuthenticate(false)
85+
err := c.refreshToken()
2186
if err != nil {
2287
return err
2388
}
@@ -74,9 +139,14 @@ func (c *fuzzitClient) GetResource(resource string) error {
74139
}
75140

76141
func (c *fuzzitClient) CreateTarget(targetName string, seedPath string) (*firestore.DocumentRef, error) {
142+
err := c.refreshToken()
143+
if err != nil {
144+
return nil, err
145+
}
146+
77147
ctx := context.Background()
78148
docRef := c.firestoreClient.Doc("orgs/" + c.Org + "/targets/" + targetName)
79-
_, err := docRef.Get(ctx)
149+
_, err = docRef.Get(ctx)
80150
if err == nil {
81151
return nil, fmt.Errorf("target %s already exist", targetName)
82152
}
@@ -97,9 +167,116 @@ func (c *fuzzitClient) CreateTarget(targetName string, seedPath string) (*firest
97167
return docRef, nil
98168
}
99169

100-
func (c *fuzzitClient) CreateJob(jobConfig Job, files []string) (*firestore.DocumentRef, error) {
170+
func (c *fuzzitClient) CreateLocalJob(jobConfig Job, files []string) error {
101171
ctx := context.Background()
172+
cli, err := client.NewClientWithOpts(client.FromEnv)
173+
if err != nil {
174+
return err
175+
}
176+
cli.NegotiateAPIVersion(ctx)
102177

178+
fuzzerPath, err := c.archiveFiles(files)
179+
if err != nil {
180+
return err
181+
}
182+
183+
fuzzer, err := os.Open(fuzzerPath)
184+
if err != nil {
185+
return err
186+
}
187+
188+
corpusPath := fmt.Sprintf("orgs/%s/targets/%s/corpus.tar.gz", c.Org, jobConfig.TargetId)
189+
log.Print(corpusPath)
190+
corpusLink, err := c.getStorageLink(corpusPath, "read")
191+
if err != nil {
192+
return err
193+
}
194+
195+
seedPath := fmt.Sprintf("orgs/%s/targets/%s/seed", c.Org, jobConfig.TargetId)
196+
seedLink, err := c.getStorageLink(seedPath, "read")
197+
if err != nil {
198+
return err
199+
}
200+
201+
log.Println("Pulling container")
202+
reader, err := cli.ImagePull(ctx, "docker.io/fuzzitdev/fuzzit:stretch-llvm9", types.ImagePullOptions{})
203+
if err != nil {
204+
return err
205+
}
206+
_, err = io.Copy(os.Stdout, reader)
207+
if err != nil {
208+
return err
209+
}
210+
211+
log.Println("Creating container")
212+
createdContainer, err := cli.ContainerCreate(ctx, &container.Config{
213+
Env: []string{
214+
"CORPUS_LINK=" + corpusLink,
215+
"SEED_LINK=" + seedLink,
216+
"ASAN_OPTIONS=" + jobConfig.AsanOptions,
217+
"UBSAN_OPTIONS=" + jobConfig.UbsanOptions,
218+
"ARGS=" + jobConfig.Args},
219+
Image: "docker.io/fuzzitdev/fuzzit:stretch-llvm9",
220+
AttachStdin: true,
221+
}, nil, nil, "")
222+
if err != nil {
223+
return err
224+
}
225+
226+
log.Println("Uploading fuzzer to container")
227+
err = cli.CopyToContainer(ctx, createdContainer.ID, "/app/", fuzzer, types.CopyToContainerOptions{
228+
AllowOverwriteDirWithFile: true,
229+
})
230+
if err != nil {
231+
return err
232+
}
233+
234+
log.Println("Starting the container")
235+
err = cli.ContainerStart(ctx, createdContainer.ID, types.ContainerStartOptions{})
236+
if err != nil {
237+
return err
238+
}
239+
240+
out, err := cli.ContainerLogs(ctx, createdContainer.ID, types.ContainerLogsOptions{
241+
ShowStdout: true,
242+
ShowStderr: true,
243+
Follow: true,
244+
})
245+
if err != nil {
246+
return err
247+
}
248+
249+
stdcopy.StdCopy(os.Stdout, os.Stderr, out)
250+
251+
log.Println("Waiting for container")
252+
statusCh, errCh := cli.ContainerWait(ctx, createdContainer.ID, container.WaitConditionNotRunning)
253+
select {
254+
case err := <-errCh:
255+
if err != nil {
256+
cli.ContainerRemove(ctx, createdContainer.ID, types.ContainerRemoveOptions{})
257+
return err
258+
}
259+
case status := <-statusCh:
260+
if status.StatusCode != 0 {
261+
cli.ContainerRemove(ctx, createdContainer.ID, types.ContainerRemoveOptions{})
262+
return fmt.Errorf("fuzzer exited with %d", status.StatusCode)
263+
}
264+
}
265+
266+
err = cli.ContainerRemove(ctx, createdContainer.ID, types.ContainerRemoveOptions{})
267+
if err != nil {
268+
return err
269+
}
270+
271+
return nil
272+
}
273+
274+
func (c *fuzzitClient) CreateJob(jobConfig Job, files []string) (*firestore.DocumentRef, error) {
275+
err := c.refreshToken()
276+
if err != nil {
277+
return nil, err
278+
}
279+
ctx := context.Background()
103280
collectionRef := c.firestoreClient.Collection("orgs/" + c.Org + "/targets/" + jobConfig.TargetId + "/jobs")
104281
fullJob := job{}
105282
fullJob.Job = jobConfig
@@ -141,7 +318,7 @@ func (c *fuzzitClient) CreateJob(jobConfig Job, files []string) (*firestore.Docu
141318

142319
storagePath := fmt.Sprintf("orgs/%s/targets/%s/jobs/%s/fuzzer", c.Org, jobConfig.TargetId, jobRef.ID)
143320
log.Println("Uploading fuzzer...")
144-
err := c.uploadFile(fuzzerPath, storagePath, "application/gzip", "fuzzer.tar.gz")
321+
err = c.uploadFile(fuzzerPath, storagePath, "application/gzip", "fuzzer.tar.gz")
145322
if err != nil {
146323
return nil, err
147324
}

0 commit comments

Comments
 (0)