Merge branch 'jt/http-redact-cookies'
authorJunio C Hamano <gitster@pobox.com>
Tue, 13 Feb 2018 21:39:11 +0000 (13:39 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 13 Feb 2018 21:39:11 +0000 (13:39 -0800)
The http tracing code, often used to debug connection issues,
learned to redact potentially sensitive information from its output
so that it can be more safely sharable.

* jt/http-redact-cookies:
http: support omitting data from traces
http: support cookie redaction when tracing

1  2 
http.c
diff --combined http.c
index 5979305bc9569c4915af5dbf9889d1f2c6d1bbe9,32a8238955a42a9f6fd86d7cd71d09129ab454a4..31755023a4f161590a63ed5597612187d31d9aa9
--- 1/http.c
--- 2/http.c
+++ b/http.c
  #include "transport.h"
  #include "packfile.h"
  #include "protocol.h"
+ #include "string-list.h"
  
  static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
+ static int trace_curl_data = 1;
+ static struct string_list cookies_to_redact = STRING_LIST_INIT_DUP;
  #if LIBCURL_VERSION_NUM >= 0x070a08
  long int git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
  #else
@@@ -575,6 -578,54 +578,54 @@@ static void redact_sensitive_header(str
                /* Everything else is opaque and possibly sensitive */
                strbuf_setlen(header,  sensitive_header - header->buf);
                strbuf_addstr(header, " <redacted>");
+       } else if (cookies_to_redact.nr &&
+                  skip_prefix(header->buf, "Cookie:", &sensitive_header)) {
+               struct strbuf redacted_header = STRBUF_INIT;
+               char *cookie;
+               while (isspace(*sensitive_header))
+                       sensitive_header++;
+               /*
+                * The contents of header starting from sensitive_header will
+                * subsequently be overridden, so it is fine to mutate this
+                * string (hence the assignment to "char *").
+                */
+               cookie = (char *) sensitive_header;
+               while (cookie) {
+                       char *equals;
+                       char *semicolon = strstr(cookie, "; ");
+                       if (semicolon)
+                               *semicolon = 0;
+                       equals = strchrnul(cookie, '=');
+                       if (!equals) {
+                               /* invalid cookie, just append and continue */
+                               strbuf_addstr(&redacted_header, cookie);
+                               continue;
+                       }
+                       *equals = 0; /* temporarily set to NUL for lookup */
+                       if (string_list_lookup(&cookies_to_redact, cookie)) {
+                               strbuf_addstr(&redacted_header, cookie);
+                               strbuf_addstr(&redacted_header, "=<redacted>");
+                       } else {
+                               *equals = '=';
+                               strbuf_addstr(&redacted_header, cookie);
+                       }
+                       if (semicolon) {
+                               /*
+                                * There are more cookies. (Or, for some
+                                * reason, the input string ends in "; ".)
+                                */
+                               strbuf_addstr(&redacted_header, "; ");
+                               cookie = semicolon + strlen("; ");
+                       } else {
+                               cookie = NULL;
+                       }
+               }
+               strbuf_setlen(header, sensitive_header - header->buf);
+               strbuf_addbuf(header, &redacted_header);
        }
  }
  
@@@ -645,24 -696,32 +696,32 @@@ static int curl_trace(CURL *handle, cur
                curl_dump_header(text, (unsigned char *)data, size, DO_FILTER);
                break;
        case CURLINFO_DATA_OUT:
-               text = "=> Send data";
-               curl_dump_data(text, (unsigned char *)data, size);
+               if (trace_curl_data) {
+                       text = "=> Send data";
+                       curl_dump_data(text, (unsigned char *)data, size);
+               }
                break;
        case CURLINFO_SSL_DATA_OUT:
-               text = "=> Send SSL data";
-               curl_dump_data(text, (unsigned char *)data, size);
+               if (trace_curl_data) {
+                       text = "=> Send SSL data";
+                       curl_dump_data(text, (unsigned char *)data, size);
+               }
                break;
        case CURLINFO_HEADER_IN:
                text = "<= Recv header";
                curl_dump_header(text, (unsigned char *)data, size, NO_FILTER);
                break;
        case CURLINFO_DATA_IN:
-               text = "<= Recv data";
-               curl_dump_data(text, (unsigned char *)data, size);
+               if (trace_curl_data) {
+                       text = "<= Recv data";
+                       curl_dump_data(text, (unsigned char *)data, size);
+               }
                break;
        case CURLINFO_SSL_DATA_IN:
-               text = "<= Recv SSL data";
-               curl_dump_data(text, (unsigned char *)data, size);
+               if (trace_curl_data) {
+                       text = "<= Recv SSL data";
+                       curl_dump_data(text, (unsigned char *)data, size);
+               }
                break;
  
        default:                /* we ignore unknown types by default */
@@@ -807,6 -866,13 +866,13 @@@ static CURL *get_curl_handle(void
        if (getenv("GIT_CURL_VERBOSE"))
                curl_easy_setopt(result, CURLOPT_VERBOSE, 1L);
        setup_curl_trace(result);
+       if (getenv("GIT_TRACE_CURL_NO_DATA"))
+               trace_curl_data = 0;
+       if (getenv("GIT_REDACT_COOKIES")) {
+               string_list_split(&cookies_to_redact,
+                                 getenv("GIT_REDACT_COOKIES"), ',', -1);
+               string_list_sort(&cookies_to_redact);
+       }
  
        curl_easy_setopt(result, CURLOPT_USERAGENT,
                user_agent ? user_agent : git_user_agent());
@@@ -2168,7 -2234,7 +2234,7 @@@ struct http_object_request *new_http_ob
        unsigned char *sha1)
  {
        char *hex = sha1_to_hex(sha1);
 -      const char *filename;
 +      struct strbuf filename = STRBUF_INIT;
        char prevfile[PATH_MAX];
        int prevlocal;
        char prev_buf[PREV_BUF_SIZE];
        hashcpy(freq->sha1, sha1);
        freq->localfile = -1;
  
 -      filename = sha1_file_name(sha1);
 +      sha1_file_name(&filename, sha1);
        snprintf(freq->tmpfile, sizeof(freq->tmpfile),
 -               "%s.temp", filename);
 +               "%s.temp", filename.buf);
  
 -      snprintf(prevfile, sizeof(prevfile), "%s.prev", filename);
 +      snprintf(prevfile, sizeof(prevfile), "%s.prev", filename.buf);
        unlink_or_warn(prevfile);
        rename(freq->tmpfile, prevfile);
        unlink_or_warn(freq->tmpfile);
 +      strbuf_release(&filename);
  
        if (freq->localfile != -1)
                error("fd leakage in start: %d", freq->localfile);
@@@ -2303,7 -2368,6 +2369,7 @@@ void process_http_object_request(struc
  int finish_http_object_request(struct http_object_request *freq)
  {
        struct stat st;
 +      struct strbuf filename = STRBUF_INIT;
  
        close(freq->localfile);
        freq->localfile = -1;
                unlink_or_warn(freq->tmpfile);
                return -1;
        }
 -      freq->rename =
 -              finalize_object_file(freq->tmpfile, sha1_file_name(freq->sha1));
 +
 +      sha1_file_name(&filename, freq->sha1);
 +      freq->rename = finalize_object_file(freq->tmpfile, filename.buf);
 +      strbuf_release(&filename);
  
        return freq->rename;
  }