Skip to content

Commit 2bd9574

Browse files
authored
fix(http): redact credentials in request logs (CodeQL clear-text logging) (#328)
1 parent bda2a73 commit 2bd9574

3 files changed

Lines changed: 22 additions & 9 deletions

File tree

files/files.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ func unzipWithResult(src, dest string, opts *UnarchiveOptions) (*Archive, error)
393393
if dirMode == 0 || dirMode&0300 != 0300 {
394394
dirMode = 0755
395395
}
396-
if err := root.MkdirAll(path, dirMode); err != nil {
396+
if err := root.MkdirAll(filepath.Clean(path), dirMode); err != nil {
397397
return archive, fmt.Errorf("failed to create directory %s: %w", path, err)
398398
}
399399
archive.Directories = append(archive.Directories, path)
@@ -601,7 +601,7 @@ func UntarWithFilterAndResult(tarball, target string, filter FileFilter, opts *U
601601
if dirMode == 0 || dirMode&0300 != 0300 {
602602
dirMode = 0755 // Default directory permissions
603603
}
604-
if err = root.MkdirAll(path, dirMode); err != nil {
604+
if err = root.MkdirAll(filepath.Clean(path), dirMode); err != nil {
605605
return archive, fmt.Errorf("failed to create directory %s: %w", path, err)
606606
}
607607
archive.Directories = append(archive.Directories, path)
@@ -676,7 +676,7 @@ func UntarWithFilterAndResult(tarball, target string, filter FileFilter, opts *U
676676
if dirMode == 0 || dirMode&0300 != 0300 {
677677
dirMode = 0755 // Default directory permissions
678678
}
679-
if err := root.MkdirAll(path, dirMode); err != nil {
679+
if err := root.MkdirAll(filepath.Clean(path), dirMode); err != nil {
680680
return archive, fmt.Errorf("failed to create directory %s: %w", path, err)
681681
}
682682
archive.Directories = append(archive.Directories, path)

http/digest.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,15 @@ func (a *digestAuth) computeA2(req *http.Request) string {
223223
return fmt.Sprintf("%s:%s", req.Method, a.uri)
224224
}
225225

226+
// hashStr hashes s with the algorithm negotiated for this digest exchange.
227+
//
228+
// MD5 and MD5-sess are retained deliberately. RFC 7616 defines MD5 as the
229+
// default digest algorithm, the algorithm is selected by the server (not the
230+
// client), and many servers only offer MD5 — so dropping it would break
231+
// authentication against those servers. SHA-256 is already used whenever the
232+
// server advertises it. The md5.New() call below is required for protocol
233+
// interoperability, not a general-purpose hash of sensitive data; any weak-hash
234+
// scanner alert on it is a known false positive for HTTP Digest authentication.
226235
func (a *digestAuth) hashStr(s string) string {
227236
var h hash.Hash
228237
alg := strings.ToUpper(a.algorithm)

http/middlewares/logger.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,9 @@ func accessURL(req *http.Request) string {
153153
u := *req.URL
154154
u.RawQuery = ""
155155
u.Fragment = ""
156-
return u.String()
156+
// Redacted() masks any password embedded in the URL userinfo so credentials
157+
// are never written to logs in clear text.
158+
return u.Redacted()
157159
}
158160

159161
func hasDetailedTrace(config TraceConfig) bool {
@@ -246,8 +248,10 @@ func jsonLogger(config TraceConfig, verbose logger.Verbose, rt http.RoundTripper
246248

247249
if err != nil {
248250
// Transport errors surface at INFO so a failed request is visible at -v=0.
249-
kv = append(kv, "error", err.Error())
250-
jsonLogAt(verbose, req, 0, kv, "%s %s error %s", req.Method, req.URL, elapsed.Truncate(time.Millisecond))
251+
// StripSecrets/Redacted keep any credentials embedded in the URL (e.g. a
252+
// url.Error wrapping "https://user:pass@host") out of the logs.
253+
kv = append(kv, "error", logger.StripSecrets(err.Error()))
254+
jsonLogAt(verbose, req, 0, kv, "%s %s error %s", req.Method, req.URL.Redacted(), elapsed.Truncate(time.Millisecond))
251255
return nil, err
252256
}
253257

@@ -272,15 +276,15 @@ func jsonLogger(config TraceConfig, verbose logger.Verbose, rt http.RoundTripper
272276
kv = append(kv, "responseBody", sanitizeBody(body))
273277
}
274278
}
275-
jsonLogAt(verbose, req, 0, kv, "%s %s %d %s", req.Method, req.URL, resp.StatusCode, elapsed.Truncate(time.Millisecond))
279+
jsonLogAt(verbose, req, 0, kv, "%s %s %d %s", req.Method, req.URL.Redacted(), resp.StatusCode, elapsed.Truncate(time.Millisecond))
276280
return resp, nil
277281
}
278282

279283
// Error-only mode suppresses the success line (see logPrettyAccess).
280284
if config.AccessLogErrorsOnly {
281285
return resp, nil
282286
}
283-
jsonLogAt(verbose, req, level, kv, "%s %s %d %s", req.Method, req.URL, resp.StatusCode, elapsed.Truncate(time.Millisecond))
287+
jsonLogAt(verbose, req, level, kv, "%s %s %d %s", req.Method, req.URL.Redacted(), resp.StatusCode, elapsed.Truncate(time.Millisecond))
284288
return resp, nil
285289
}
286290

@@ -368,7 +372,7 @@ func logPrettyAccess(config TraceConfig, verbose logger.Verbose, req *http.Reque
368372
// error-only mode at -v=0; the body captures the cause (e.g. an HTML 404/500
369373
// page) without raising verbosity.
370374
if err != nil {
371-
logAt(verbose, req, 0, "%s %s %s %s", method, url, console.Redf("error: %s", err.Error()), dur)
375+
logAt(verbose, req, 0, "%s %s %s %s", method, url, console.Redf("error: %s", logger.StripSecrets(err.Error())), dur)
372376
return
373377
}
374378
statusCode := 0

0 commit comments

Comments
 (0)