github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/dashboard/app/api_test.go (about) 1 // Copyright 2017 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package main 5 6 import ( 7 "context" 8 "slices" 9 "sort" 10 "testing" 11 "time" 12 13 "github.com/google/syzkaller/dashboard/dashapi" 14 "github.com/google/syzkaller/sys/targets" 15 "github.com/stretchr/testify/assert" 16 ) 17 18 func TestClientSecretOK(t *testing.T) { 19 got, err := checkClient(&GlobalConfig{ 20 Clients: map[string]string{ 21 "user": "secr1t", 22 }, 23 }, "user", "secr1t", "") 24 if err != nil || got != "" { 25 t.Errorf("unexpected error %v %v", got, err) 26 } 27 } 28 29 func TestClientOauthOK(t *testing.T) { 30 got, err := checkClient(&GlobalConfig{ 31 Clients: map[string]string{ 32 "user": "OauthSubject:public", 33 }, 34 }, "user", "", "OauthSubject:public") 35 if err != nil || got != "" { 36 t.Errorf("unexpected error %v %v", got, err) 37 } 38 } 39 40 func TestClientSecretFail(t *testing.T) { 41 got, err := checkClient(&GlobalConfig{ 42 Clients: map[string]string{ 43 "user": "secr1t", 44 }, 45 }, "user", "wrong", "") 46 if err != ErrAccess || got != "" { 47 t.Errorf("unexpected error %v %v", got, err) 48 } 49 } 50 51 func TestClientSecretMissing(t *testing.T) { 52 got, err := checkClient(&GlobalConfig{ 53 Clients: map[string]string{}, 54 }, "user", "ignored", "") 55 if err != ErrAccess || got != "" { 56 t.Errorf("unexpected error %v %v", got, err) 57 } 58 } 59 60 func TestClientNamespaceOK(t *testing.T) { 61 got, err := checkClient(&GlobalConfig{ 62 Namespaces: map[string]*Config{ 63 "ns1": { 64 Clients: map[string]string{ 65 "user": "secr1t", 66 }, 67 }, 68 }, 69 }, "user", "secr1t", "") 70 if err != nil || got != "ns1" { 71 t.Errorf("unexpected error %v %v", got, err) 72 } 73 } 74 75 func TestEmergentlyStoppedEmail(t *testing.T) { 76 c := NewCtx(t) 77 defer c.Close() 78 79 client := c.publicClient 80 build := testBuild(1) 81 client.UploadBuild(build) 82 83 crash := testCrash(build, 1) 84 client.ReportCrash(crash) 85 86 c.advanceTime(time.Hour) 87 _, err := c.AuthGET(AccessAdmin, "/admin?action=emergency_stop") 88 c.expectOK(err) 89 90 // There should be no email. 91 c.advanceTime(time.Hour) 92 c.expectNoEmail() 93 } 94 95 func TestEmergentlyStoppedReproEmail(t *testing.T) { 96 c := NewCtx(t) 97 defer c.Close() 98 99 client := c.publicClient 100 build := testBuild(1) 101 client.UploadBuild(build) 102 103 crash := testCrash(build, 1) 104 client.ReportCrash(crash) 105 c.pollEmailBug() 106 107 crash2 := testCrash(build, 1) 108 crash2.ReproOpts = []byte("repro opts") 109 crash2.ReproSyz = []byte("getpid()") 110 client.ReportCrash(crash2) 111 112 c.advanceTime(time.Hour) 113 _, err := c.AuthGET(AccessAdmin, "/admin?action=emergency_stop") 114 c.expectOK(err) 115 116 // There should be no email. 117 c.advanceTime(time.Hour) 118 c.expectNoEmail() 119 } 120 121 func TestEmergentlyStoppedExternalReport(t *testing.T) { 122 c := NewCtx(t) 123 defer c.Close() 124 125 client := c.client 126 build := testBuild(1) 127 client.UploadBuild(build) 128 129 crash := testCrash(build, 1) 130 client.ReportCrash(crash) 131 132 c.advanceTime(time.Hour) 133 _, err := c.AuthGET(AccessAdmin, "/admin?action=emergency_stop") 134 c.expectOK(err) 135 136 // There should be no email. 137 c.advanceTime(time.Hour) 138 client.pollBugs(0) 139 } 140 141 func TestEmergentlyStoppedEmailJob(t *testing.T) { 142 c := NewCtx(t) 143 defer c.Close() 144 145 client := c.publicClient 146 build := testBuild(1) 147 client.UploadBuild(build) 148 149 crash := testCrash(build, 1) 150 crash.ReproOpts = []byte("repro opts") 151 crash.ReproSyz = []byte("getpid()") 152 client.ReportCrash(crash) 153 sender := c.pollEmailBug().Sender 154 c.incomingEmail(sender, "#syz upstream\n") 155 sender = c.pollEmailBug().Sender 156 157 // Send a patch testing request. 158 c.advanceTime(time.Hour) 159 c.incomingEmail(sender, syzTestGitBranchSamplePatch, 160 EmailOptMessageID(1), EmailOptFrom("test@requester.com"), 161 EmailOptCC([]string{"somebody@else.com", "test@syzkaller.com"})) 162 c.expectNoEmail() 163 164 // Emulate a finished job. 165 pollResp := client.pollJobs(build.Manager) 166 c.expectEQ(pollResp.Type, dashapi.JobTestPatch) 167 168 c.advanceTime(time.Hour) 169 jobDoneReq := &dashapi.JobDoneReq{ 170 ID: pollResp.ID, 171 Build: *build, 172 CrashTitle: "test crash title", 173 CrashLog: []byte("test crash log"), 174 CrashReport: []byte("test crash report"), 175 } 176 client.JobDone(jobDoneReq) 177 178 // Now we emergently stop syzbot. 179 c.advanceTime(time.Hour) 180 _, err := c.AuthGET(AccessAdmin, "/admin?action=emergency_stop") 181 c.expectOK(err) 182 183 // There should be no email. 184 c.advanceTime(time.Hour) 185 c.expectNoEmail() 186 } 187 188 func TestEmergentlyStoppedCrashReport(t *testing.T) { 189 c := NewCtx(t) 190 defer c.Close() 191 192 client := c.publicClient 193 build := testBuild(1) 194 client.UploadBuild(build) 195 196 // Now we emergently stop syzbot. 197 c.advanceTime(time.Hour) 198 _, err := c.AuthGET(AccessAdmin, "/admin?action=emergency_stop") 199 c.expectOK(err) 200 201 crash := testCrash(build, 1) 202 crash.ReproOpts = []byte("repro opts") 203 crash.ReproSyz = []byte("getpid()") 204 client.ReportCrash(crash) 205 206 listResp, err := client.BugList() 207 c.expectOK(err) 208 c.expectEQ(len(listResp.List), 0) 209 } 210 211 func TestUpdateReportingPriority(t *testing.T) { 212 bug := &Bug{ 213 Namespace: testConfig.DefaultNamespace, 214 Title: "bug", 215 } 216 build := Build{ 217 Namespace: testConfig.DefaultNamespace, 218 KernelRepo: "git://syzkaller.org", 219 KernelBranch: "branch10", 220 } 221 222 crashes := []*Crash{ 223 // This group of crashes should have the same priority. 224 // Revoked and no repro. 225 { 226 BuildID: "0", 227 ReproIsRevoked: true, 228 }, 229 // Non-revoked and no repro. 230 { 231 BuildID: "1", 232 }, 233 // Revoked but has syz repro. 234 { 235 BuildID: "2", 236 ReproIsRevoked: true, 237 ReproSyz: 1, 238 }, 239 // Revoked but has C repro. 240 { 241 BuildID: "3", 242 ReproIsRevoked: true, 243 ReproC: 1, 244 }, 245 246 // This group of crashes should have the same priority. 247 // Non-revoked and no repro but title matches with bug. 248 { 249 BuildID: "4", 250 Title: bug.Title, 251 }, 252 // Revoked and has C repro and title matches with bug. 253 { 254 BuildID: "5", 255 ReproC: 1, 256 Title: bug.Title, 257 ReproIsRevoked: true, 258 }, 259 260 // Non-revoked and has syz repro. 261 { 262 BuildID: "6", 263 ReproSyz: 1, 264 }, 265 // Non-revoked and has C repro. 266 { 267 BuildID: "7", 268 ReproC: 1, 269 }, 270 // Non-revoked and has C repro and title matches with bug. 271 { 272 BuildID: "8", 273 ReproC: 1, 274 Title: bug.Title, 275 }, 276 // Last. Non-revoked, has C repro, title matches with bug and arch is AMD64. 277 { 278 BuildID: "9", 279 ReproC: 1, 280 Title: bug.Title, 281 }, 282 } 283 284 ctx := context.Background() 285 for i, crash := range crashes { 286 crash.Manager = "special-obsoleting" 287 if i == len(crashes)-1 { 288 build.Arch = targets.AMD64 289 } 290 crash.UpdateReportingPriority(ctx, &build, bug) 291 } 292 293 assert.True(t, sort.SliceIsSorted(crashes, func(i, j int) bool { 294 return crashes[i].BuildID < crashes[j].BuildID 295 })) 296 297 var prios []int64 298 for _, crash := range crashes { 299 prios = append(prios, crash.ReportLen) 300 } 301 302 // "0-3", "4-5" have the same priority (repro revoked as no repro). 303 assert.Equal(t, len(slices.Compact(prios)), len(prios)-4) 304 } 305 306 func TestCreateUploadURL(t *testing.T) { 307 c := NewCtx(t) 308 defer c.Close() 309 310 c.transformContext = func(c context.Context) context.Context { 311 newConfig := *getConfig(c) 312 newConfig.UploadBucket = "blobstorage" 313 return contextWithConfig(c, &newConfig) 314 } 315 316 url, err := c.client.CreateUploadURL() 317 assert.NoError(t, err) 318 assert.Regexp(t, "blobstorage/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}.upload", url) 319 }