@@ -11,7 +11,6 @@ import (
11
11
"time"
12
12
13
13
"github.com/go-errors/errors"
14
- "github.com/go-logr/logr"
15
14
"github.com/trufflesecurity/trufflehog/v3/pkg/log"
16
15
"google.golang.org/protobuf/proto"
17
16
"google.golang.org/protobuf/types/known/anypb"
@@ -37,7 +36,6 @@ type Source struct {
37
36
user string
38
37
token string
39
38
header * header
40
- log logr.Logger
41
39
client * http.Client
42
40
sources.Progress
43
41
}
@@ -66,8 +64,6 @@ func (s *Source) JobID() sources.JobID {
66
64
67
65
// Init returns an initialized Jenkins source.
68
66
func (s * Source ) Init (aCtx context.Context , name string , jobId sources.JobID , sourceId sources.SourceID , verify bool , connection * anypb.Any , _ int ) error {
69
- s .log = aCtx .Logger ()
70
-
71
67
s .name = name
72
68
s .sourceId = sourceId
73
69
s .jobId = jobId
@@ -89,7 +85,7 @@ func (s *Source) Init(aCtx context.Context, name string, jobId sources.JobID, so
89
85
90
86
const retryDelay = time .Second * 30
91
87
opts = append (opts ,
92
- roundtripper .WithLogger (s . log ),
88
+ roundtripper .WithLogger (aCtx . Logger () ),
93
89
roundtripper .WithLogging (),
94
90
roundtripper .WithRetryable (
95
91
roundtripper .WithShouldRetry5XXDuration (retryDelay ),
@@ -104,6 +100,7 @@ func (s *Source) Init(aCtx context.Context, name string, jobId sources.JobID, so
104
100
s .client = client
105
101
106
102
var unparsedURL string
103
+ var authMethod string
107
104
switch cred := conn .GetCredential ().(type ) {
108
105
case * sourcespb.Jenkins_BasicAuth :
109
106
unparsedURL = conn .Endpoint
@@ -113,15 +110,18 @@ func (s *Source) Init(aCtx context.Context, name string, jobId sources.JobID, so
113
110
return errors .Errorf ("Jenkins source basic auth credential requires 'password' to be specified" )
114
111
}
115
112
log .RedactGlobally (s .token )
113
+ authMethod = "basic"
116
114
case * sourcespb.Jenkins_Header :
117
115
unparsedURL = conn .Endpoint
118
116
s .header = & header {
119
117
key : cred .Header .Key ,
120
118
value : cred .Header .Value ,
121
119
}
122
120
log .RedactGlobally (cred .Header .GetValue ())
121
+ authMethod = "header"
123
122
case * sourcespb.Jenkins_Unauthenticated :
124
123
unparsedURL = conn .Endpoint
124
+ authMethod = "none"
125
125
default :
126
126
return errors .Errorf ("unknown or unspecified authentication method provided for Jenkins source %q (unauthenticated scans must be explicitly configured)" , name )
127
127
}
@@ -131,6 +131,11 @@ func (s *Source) Init(aCtx context.Context, name string, jobId sources.JobID, so
131
131
return errors .WrapPrefix (err , fmt .Sprintf ("Invalid endpoint URL given for Jenkins source: %s" , unparsedURL ), 0 )
132
132
}
133
133
134
+ aCtx .Logger ().V (1 ).Info ("initialized Jenkins source" ,
135
+ "auth_method" , authMethod ,
136
+ "url_raw" , unparsedURL ,
137
+ "url_parsed" , s .url .String ())
138
+
134
139
return nil
135
140
}
136
141
@@ -187,13 +192,24 @@ func (s *Source) GetJenkinsJobs(ctx context.Context) (JenkinsJobResponse, error)
187
192
}
188
193
189
194
func (s * Source ) RecursivelyGetJenkinsObjectsForPath (ctx context.Context , absolutePath string ) (JenkinsJobResponse , error ) {
195
+ ctx .Logger ().V (3 ).Info ("getting objects" ,
196
+ "path" , absolutePath )
197
+
190
198
jobs := JenkinsJobResponse {}
191
199
objects , err := s .GetJenkinsObjectsForPath (ctx , absolutePath )
192
200
if err != nil {
193
201
return jobs , err
194
202
}
203
+ ctx .Logger ().V (3 ).Info ("got objects" ,
204
+ "path" , absolutePath ,
205
+ "count" , len (objects .Jobs ))
195
206
196
207
for _ , job := range objects .Jobs {
208
+ ctx .Logger ().V (3 ).Info ("processing object" ,
209
+ "object_name" , job .Name ,
210
+ "object_class" , job .Class ,
211
+ "object_url" , job .Url )
212
+
197
213
if job .Class == "com.cloudbees.hudson.plugins.folder.Folder" {
198
214
u , err := url .Parse (job .Url )
199
215
if err != nil {
@@ -221,6 +237,7 @@ func (s *Source) GetJenkinsObjectsForPath(ctx context.Context, absolutePath stri
221
237
baseUrl .Path = path .Join (absolutePath , "api/json" )
222
238
baseUrl .RawQuery = fmt .Sprintf ("tree=jobs[name,url]{%d,%d}" , i , i + 100 )
223
239
240
+ ctx .Logger ().V (4 ).Info ("executing query" , "query_url" , baseUrl .String ())
224
241
req , err := s .NewRequest (http .MethodGet , baseUrl .String (), nil )
225
242
if err != nil {
226
243
return res , errors .WrapPrefix (err , "Failed to create new request to get jenkins jobs" , 0 )
@@ -255,6 +272,11 @@ func (s *Source) GetJenkinsObjectsForPath(ctx context.Context, absolutePath stri
255
272
}
256
273
257
274
func (s * Source ) GetJenkinsBuilds (ctx context.Context , jobAbsolutePath string ) (JenkinsBuildResponse , error ) {
275
+ ctx = context .WithValues (ctx ,
276
+ "job_path" , jobAbsolutePath )
277
+
278
+ ctx .Logger ().V (2 ).Info ("getting builds" )
279
+
258
280
builds := JenkinsBuildResponse {}
259
281
buildsUrl := * s .url
260
282
for i := 0 ; true ; i += 100 {
@@ -265,6 +287,7 @@ func (s *Source) GetJenkinsBuilds(ctx context.Context, jobAbsolutePath string) (
265
287
return builds , errors .WrapPrefix (err , "Failed to create new request to get jenkins builds" , 0 )
266
288
}
267
289
290
+ ctx .Logger ().V (4 ).Info ("executing query" , "query_url" , req .URL .String ())
268
291
resp , err := s .client .Do (req )
269
292
if err != nil {
270
293
return builds , errors .WrapPrefix (err , "Failed to do get jenkins builds request" , 0 )
@@ -299,34 +322,48 @@ func (s *Source) Chunks(ctx context.Context, chunksChan chan *sources.Chunk, _ .
299
322
if err != nil {
300
323
return errors .WrapPrefix (err , "Failed to get Jenkins job response" , 0 )
301
324
}
325
+ ctx .Logger ().V (1 ).Info ("got jobs" , "count" , len (jobs .Jobs ))
302
326
303
327
for i , project := range jobs .Jobs {
304
328
if common .IsDone (ctx ) {
305
329
return nil
306
330
}
307
331
332
+ ctx := context .WithValues (ctx ,
333
+ "job_name" , project .Name ,
334
+ "job_class" , project .Class ,
335
+ "job_url" , project .Url )
336
+
308
337
s .SetProgressComplete (i , len (jobs .Jobs ), fmt .Sprintf ("Project: %s" , project .Name ), "" )
309
338
310
339
parsedUrl , err := url .Parse (project .Url )
311
340
if err != nil {
312
- s . log .Error (err , "Failed to parse Jenkins project URL, skipping project" , "url" , project . Url , "project" , project . Name )
341
+ ctx . Logger () .Error (err , "failed to parse job URL; skipping job" )
313
342
continue
314
343
}
315
344
projectURL := * s .url
316
345
projectURL .Path = parsedUrl .Path
317
346
318
347
builds , err := s .GetJenkinsBuilds (ctx , projectURL .Path )
319
348
if err != nil {
320
- s . log .Error (err , "Failed to get Jenkins build response, skipping project" , "project" , project . Name )
349
+ ctx . Logger () .Error (err , "failed to get builds; skipping job" )
321
350
continue
322
351
}
352
+ ctx .Logger ().V (2 ).Info ("got builds" ,
353
+ "count" , len (builds .Builds ))
323
354
324
355
for _ , build := range builds .Builds {
325
356
if common .IsDone (ctx ) {
326
357
return nil
327
358
}
328
359
329
- s .chunkBuild (ctx , build , project .Name , chunksChan )
360
+ ctx := context .WithValues (ctx ,
361
+ "build_number" , build .Number ,
362
+ "build_url" , build .Url )
363
+
364
+ if err := s .chunkBuild (ctx , build , project .Name , chunksChan ); err != nil {
365
+ ctx .Logger ().Error (err , "error scanning build log" )
366
+ }
330
367
}
331
368
}
332
369
@@ -336,45 +373,46 @@ func (s *Source) Chunks(ctx context.Context, chunksChan chan *sources.Chunk, _ .
336
373
337
374
// chunkBuild takes build information and sends it to the chunksChan.
338
375
// It also logs all errors that occur and does not return them, as the parent context expects to continue running.
339
- func (s * Source ) chunkBuild (_ context.Context , build JenkinsBuild , projectName string , chunksChan chan * sources.Chunk ) {
340
- // Setup a logger to identify the build and project.
341
- chunkBuildLog := s .log .WithValues (
342
- "build" , build .Number ,
343
- "project" , projectName ,
344
- )
376
+ func (s * Source ) chunkBuild (
377
+ ctx context.Context ,
378
+ build JenkinsBuild ,
379
+ projectName string ,
380
+ chunksChan chan * sources.Chunk ,
381
+ ) error {
382
+ ctx .Logger ().V (2 ).Info ("chunking build" )
345
383
346
384
parsedUrl , err := url .Parse (build .Url )
347
385
if err != nil {
348
- chunkBuildLog .Error (err , "Failed to parse Jenkins build URL, skipping build" , "url" , build .Url )
349
- return
386
+ return fmt .Errorf ("failed to parse build URL %q: %w" , build .Url , err )
350
387
}
351
388
buildLogURL := * s .url
352
389
buildLogURL .Path = path .Join (parsedUrl .Path , "consoleText" )
390
+ ctx = context .WithValues (ctx ,
391
+ "build_log_url" , buildLogURL .String ())
353
392
354
393
req , err := s .NewRequest (http .MethodGet , buildLogURL .String (), nil )
355
394
if err != nil {
356
- chunkBuildLog .Error (err , "Failed to create new request to Jenkins, skipping build" )
357
- return
395
+ return fmt .Errorf ("failed to create HTTP request to %q: %w" , buildLogURL .String (), err )
358
396
}
359
397
360
398
resp , err := s .client .Do (req )
361
399
if err != nil {
362
- chunkBuildLog .Error (err , "Failed to get build log in Jenkins chunks, skipping build" )
363
- return
400
+ return fmt .Errorf ("could not retrieve build log from %q: %w" , buildLogURL .String (), err )
364
401
}
365
402
defer resp .Body .Close ()
366
403
367
404
if resp .StatusCode >= 400 {
368
- chunkBuildLog .Error (err , "Status Code from build was unexpected, skipping build" , "status_code" , resp .StatusCode )
369
- return
405
+ return fmt .Errorf ("got unexpected HTTP status code %v when trying to retrieve build log from %q" ,
406
+ resp .StatusCode ,
407
+ buildLogURL .String ())
370
408
}
371
409
372
410
buildLog , err := io .ReadAll (resp .Body )
373
411
if err != nil {
374
- chunkBuildLog .Error (err , "Failed to read body from the build log response, skipping build" )
375
- return
412
+ return fmt .Errorf ("error reading build log response body from %q: %w" , buildLogURL .String (), err )
376
413
}
377
414
415
+ ctx .Logger ().V (4 ).Info ("scanning build log" )
378
416
chunksChan <- & sources.Chunk {
379
417
SourceName : s .name ,
380
418
SourceID : s .SourceID (),
@@ -392,6 +430,8 @@ func (s *Source) chunkBuild(_ context.Context, build JenkinsBuild, projectName s
392
430
Data : buildLog ,
393
431
Verify : s .verify ,
394
432
}
433
+
434
+ return nil
395
435
}
396
436
397
437
type JenkinsJobResponse struct {
0 commit comments