{"thread":{"id":"62668","subject":"Fwd: [Bug] `credential fill` prints incomplete bearer credential","startedAt":"2024-12-18T20:43:14Z","lastAt":"2024-12-21T08:09:12Z","messageCount":6,"participants":["M Hickford","brian m. carlson","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"509312","messageId":"CAGJzqsnAO_vDSYOC7ZbYUQHJ3fT10JuQ2RrC2cvY4hcPMnZ=bg@mail.gmail.com","threadId":"62668","inReplyTo":"CAGJzqs=ksKqY2M8Px3uv6ut=MBwkmpPUqp3xVzVpNMJ0YBrBww@mail.gmail.com","subject":"Fwd: [Bug] `credential fill` prints incomplete bearer credential","fromName":"M Hickford","fromEmail":"mirth.hickford@gmail.com","sentAt":"2024-12-18T20:42:31Z","receivedAt":"2024-12-18T20:43:14Z","isPatch":false,"sender":{"key":"mirth.hickford@gmail.com","avatar":"https://avatars.githubusercontent.com/u/105314?v=4"},"body":"Hi. Is this a bug in git version 2.47.1? Or am I using it incorrectly?\n\n# erase existing example.com credentials\nprintf \"host=example.com\\nprotocol=https\\n\" | git -c\ncredential.helper= -c credential.helper=cache credential reject\n# store bearer token with expiry in far future in credential-cache\nprintf \"host=example.com\\nprotocol=https\\nauthtype=bearer\\ncredential=letmein\\npassword_expiry_utc=2147483640\\n\"\n| git credential-cache store\n# try to retrieve credential\nprintf \"host=example.com\\nprotocol=https\\n\" | git -c\ncredential.helper= -c credential.helper=cache credential fill\n\nExpected output (complete credential):\n\nprotocol=https\nhost=example.com\nauthtype=bearer\ncredential=letmein\npassword_expiry_utc=2147483640\n\nActual output (incomplete credential, no prompt for username or password):\n\nprotocol=https\nhost=example.com\npassword_expiry_utc=2147483640\n"},{"id":"509318","messageId":"Z2N-rV4fhF3ZkGlp@tapette.crustytoothpaste.net","threadId":"62668","inReplyTo":"CAGJzqsnAO_vDSYOC7ZbYUQHJ3fT10JuQ2RrC2cvY4hcPMnZ=bg@mail.gmail.com","subject":"Re: Fwd: [Bug] `credential fill` prints incomplete bearer credential","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-12-19T02:02:21Z","receivedAt":"2024-12-19T02:02:29Z","isPatch":false,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-12-18 at 20:42:31, M Hickford wrote:\n> Hi. Is this a bug in git version 2.47.1? Or am I using it incorrectly?\n> \n> # erase existing example.com credentials\n> printf \"host=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential reject\n> # store bearer token with expiry in far future in credential-cache\n> printf \"host=example.com\\nprotocol=https\\nauthtype=bearer\\ncredential=letmein\\npassword_expiry_utc=2147483640\\n\"\n> | git credential-cache store\n> # try to retrieve credential\n> printf \"host=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential fill\n> \n> Expected output (complete credential):\n> \n> protocol=https\n> host=example.com\n> authtype=bearer\n> credential=letmein\n> password_expiry_utc=2147483640\n> \n> Actual output (incomplete credential, no prompt for username or password):\n> \n> protocol=https\n> host=example.com\n> password_expiry_utc=2147483640\n\nThis is expected.  Every request to a credential helper should include\nall of the capabilities that the caller supports on input, and the\ncredential helper will always emit those on output.  `git credential`,\nhowever, will only emit the capabilities that were actually supported,\nso that general callers (including Git LFS) can determine the actual\nset of supported capabilities.\n\nIn this case, you asked the cache helper for a credential, but didn't\ntell it that you supported `authtype` and `credential`.  Therefore, the\nonly safe thing it can assume is that you are incapable of parsing and\nunderstanding those fields, so it doesn't emit them.  This is a benefit\nfor security, because some tooling logs all fields but the `password`\nfield, and we don't want to include new secret fields that the caller is\ngoing to shovel into a file or syslog.\n\nIn addition, the helper could actually store two different sets of\ncredentials, one which is a username and password, and one which is an\nauthtype and credential.  If you provided the capability, the latter\nwould be omitted, but otherwise the former would.  That can be helpful\nif you have a stronger credential type but might occasionally need to\nuse older software (say, older versions of Git or Git LFS).\n\nHowever, if you provide the proper capability, this works as you expect:\n\n----\n% printf \"host=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential reject\n% printf \"capability[]=authtype\\nhost=example.com\\nprotocol=https\\nauthtype=bearer\\ncredential=letmein\\npassword_expiry_utc=2147483640\\n\" | git credential-cache store\n% printf \"capability[]=authtype\\nhost=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential fill\ncapability[]=authtype\nauthtype=bearer\ncredential=letmein\nprotocol=https\nhost=example.com\npassword_expiry_utc=2147483640\n----\n\nNote that `capability[]` directives should always start the request to\nallow one-pass parsing.\n\nHopefully this is helpful.\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"509326","messageId":"xmqqldwcnrlz.fsf@gitster.g","threadId":"62668","inReplyTo":"Z2N-rV4fhF3ZkGlp@tapette.crustytoothpaste.net","subject":"Re: Fwd: [Bug] `credential fill` prints incomplete bearer credential","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-12-19T05:56:08Z","receivedAt":"2024-12-19T05:56:11Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> On 2024-12-18 at 20:42:31, M Hickford wrote:\n>> Hi. Is this a bug in git version 2.47.1? Or am I using it incorrectly?\n>> \n>> # erase existing example.com credentials\n>> printf \"host=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential reject\n>> # store bearer token with expiry in far future in credential-cache\n>> printf \"host=example.com\\nprotocol=https\\nauthtype=bearer\\ncredential=letmein\\npassword_expiry_utc=2147483640\\n\"\n>> | git credential-cache store\n>> # try to retrieve credential\n>> printf \"host=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential fill\n>> \n>> Expected output (complete credential):\n>> \n>> protocol=https\n>> host=example.com\n>> authtype=bearer\n>> credential=letmein\n>> password_expiry_utc=2147483640\n>> \n>> Actual output (incomplete credential, no prompt for username or password):\n>> \n>> protocol=https\n>> host=example.com\n>> password_expiry_utc=2147483640\n>\n> This is expected.  Every request to a credential helper should include\n> all of the capabilities that the caller supports on input, and the\n> credential helper will always emit those on output.  `git credential`,\n> however, will only emit the capabilities that were actually supported,\n> so that general callers (including Git LFS) can determine the actual\n> set of supported capabilities.\n> ...\n\nThe original report did\n\n - run \"git credential reject\" to clear\n - run \"git credential-cache store\"!!\n - run \"git credential fill\" to check\n\nwhich looked curious.\n\nAm I correct to understand that if the second step is replaced with\n\"git credential approve\" (which calls \"credential-cache store\"\ninternally), the right thing happens and necessary capabilities are\npassed?\n\nThanks.\n"},{"id":"509344","messageId":"CAGJzqsksGy4Koz-RbqKebSVL7DO_WP6RvhXqug-f0EPhTGK+ng@mail.gmail.com","threadId":"62668","inReplyTo":"Z2N-rV4fhF3ZkGlp@tapette.crustytoothpaste.net","subject":"Re: Fwd: [Bug] `credential fill` prints incomplete bearer credential","fromName":"M Hickford","fromEmail":"mirth.hickford@gmail.com","sentAt":"2024-12-19T19:12:00Z","receivedAt":"2024-12-19T19:12:43Z","isPatch":false,"sender":{"key":"mirth.hickford@gmail.com","avatar":"https://avatars.githubusercontent.com/u/105314?v=4"},"body":"On Thu, 19 Dec 2024 at 02:02, brian m. carlson\n<sandals@crustytoothpaste.net> wrote:\n>\n> On 2024-12-18 at 20:42:31, M Hickford wrote:\n> > Hi. Is this a bug in git version 2.47.1? Or am I using it incorrectly?\n> >\n> > # erase existing example.com credentials\n> > printf \"host=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential reject\n> > # store bearer token with expiry in far future in credential-cache\n> > printf \"host=example.com\\nprotocol=https\\nauthtype=bearer\\ncredential=letmein\\npassword_expiry_utc=2147483640\\n\"\n> > | git credential-cache store\n> > # try to retrieve credential\n> > printf \"host=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential fill\n> >\n> > Expected output (complete credential):\n> >\n> > protocol=https\n> > host=example.com\n> > authtype=bearer\n> > credential=letmein\n> > password_expiry_utc=2147483640\n> >\n> > Actual output (incomplete credential, no prompt for username or password):\n> >\n> > protocol=https\n> > host=example.com\n> > password_expiry_utc=2147483640\n>\n> This is expected.  Every request to a credential helper should include\n> all of the capabilities that the caller supports on input, and the\n> credential helper will always emit those on output.  `git credential`,\n> however, will only emit the capabilities that were actually supported,\n> so that general callers (including Git LFS) can determine the actual\n> set of supported capabilities.\n>\n> In this case, you asked the cache helper for a credential, but didn't\n> tell it that you supported `authtype` and `credential`.  Therefore, the\n> only safe thing it can assume is that you are incapable of parsing and\n> understanding those fields, so it doesn't emit them.  This is a benefit\n> for security, because some tooling logs all fields but the `password`\n> field, and we don't want to include new secret fields that the caller is\n> going to shovel into a file or syslog.\n\nThank you Brian for the explanation. That fits my example.\n\nTypically `credential fill` prompts the user if it can't complete a\ncredential. Surely it should prompt in this case too?\n\nhttps://git-scm.com/docs/git-credential \"git-credential will attempt\nto add \"username\" and \"password\" attributes to the description by\nreading config files, by contacting any configured credential helpers,\nor by prompting the user\"\n\n>\n> In addition, the helper could actually store two different sets of\n> credentials, one which is a username and password, and one which is an\n> authtype and credential.  If you provided the capability, the latter\n> would be omitted, but otherwise the former would.  That can be helpful\n> if you have a stronger credential type but might occasionally need to\n> use older software (say, older versions of Git or Git LFS).\n>\n> However, if you provide the proper capability, this works as you expect:\n>\n> ----\n> % printf \"host=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential reject\n> % printf \"capability[]=authtype\\nhost=example.com\\nprotocol=https\\nauthtype=bearer\\ncredential=letmein\\npassword_expiry_utc=2147483640\\n\" | git credential-cache store\n> % printf \"capability[]=authtype\\nhost=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential fill\n> capability[]=authtype\n> authtype=bearer\n> credential=letmein\n> protocol=https\n> host=example.com\n> password_expiry_utc=2147483640\n> ----\n>\n> Note that `capability[]` directives should always start the request to\n> allow one-pass parsing.\n>\n> Hopefully this is helpful.\n> --\n> brian m. carlson (they/them or he/him)\n> Toronto, Ontario, CA\n"},{"id":"509345","messageId":"CAGJzqsmQY9p+nxHPB67N1P4i361adMbvT-BNR7UKfsSY=QGi2A@mail.gmail.com","threadId":"62668","inReplyTo":"Z2N-rV4fhF3ZkGlp@tapette.crustytoothpaste.net","subject":"Re: Fwd: [Bug] `credential fill` prints incomplete bearer credential","fromName":"M Hickford","fromEmail":"mirth.hickford@gmail.com","sentAt":"2024-12-19T19:15:00Z","receivedAt":"2024-12-19T19:16:00Z","isPatch":false,"sender":{"key":"mirth.hickford@gmail.com","avatar":"https://avatars.githubusercontent.com/u/105314?v=4"},"body":"On Thu, 19 Dec 2024 at 02:02, brian m. carlson\n<sandals@crustytoothpaste.net> wrote:\n>\n> On 2024-12-18 at 20:42:31, M Hickford wrote:\n> > Hi. Is this a bug in git version 2.47.1? Or am I using it incorrectly?\n> >\n> > # erase existing example.com credentials\n> > printf \"host=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential reject\n> > # store bearer token with expiry in far future in credential-cache\n> > printf \"host=example.com\\nprotocol=https\\nauthtype=bearer\\ncredential=letmein\\npassword_expiry_utc=2147483640\\n\"\n> > | git credential-cache store\n> > # try to retrieve credential\n> > printf \"host=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential fill\n> >\n> > Expected output (complete credential):\n> >\n> > protocol=https\n> > host=example.com\n> > authtype=bearer\n> > credential=letmein\n> > password_expiry_utc=2147483640\n> >\n> > Actual output (incomplete credential, no prompt for username or password):\n> >\n> > protocol=https\n> > host=example.com\n> > password_expiry_utc=2147483640\n>\n> This is expected.  Every request to a credential helper should include\n> all of the capabilities that the caller supports on input, and the\n> credential helper will always emit those on output.  `git credential`,\n> however, will only emit the capabilities that were actually supported,\n> so that general callers (including Git LFS) can determine the actual\n> set of supported capabilities.\n>\n> In this case, you asked the cache helper for a credential, but didn't\n> tell it that you supported `authtype` and `credential`.  Therefore, the\n> only safe thing it can assume is that you are incapable of parsing and\n> understanding those fields, so it doesn't emit them.  This is a benefit\n> for security, because some tooling logs all fields but the `password`\n> field, and we don't want to include new secret fields that the caller is\n> going to shovel into a file or syslog.\n>\n> In addition, the helper could actually store two different sets of\n> credentials, one which is a username and password, and one which is an\n> authtype and credential.  If you provided the capability, the latter\n> would be omitted, but otherwise the former would.  That can be helpful\n> if you have a stronger credential type but might occasionally need to\n> use older software (say, older versions of Git or Git LFS).\n>\n> However, if you provide the proper capability, this works as you expect:\n>\n> ----\n> % printf \"host=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential reject\n> % printf \"capability[]=authtype\\nhost=example.com\\nprotocol=https\\nauthtype=bearer\\ncredential=letmein\\npassword_expiry_utc=2147483640\\n\" | git credential-cache store\n> % printf \"capability[]=authtype\\nhost=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential fill\n> capability[]=authtype\n> authtype=bearer\n> credential=letmein\n> protocol=https\n> host=example.com\n> password_expiry_utc=2147483640\n> ----\n>\n> Note that `capability[]` directives should always start the request to\n> allow one-pass parsing.\n\nI think a bug exists in credential-cache. Below it receives a query\n*without* capability authtype, upgrades it *with* capability authtype\nand prints a bearer credential.\n\ngit credential-cache exit\n# store bearer credential\nprintf \"capability[]=authtype\\nhost=example.com\\nprotocol=https\\nauthtype=bearer\\ncredential=letmein\\npassword_expiry_utc=2147483640\\n\"\n| git credential-cache store\n# query with capability authtype (prints bearer credential as expected)\nprintf \"capability[]=authtype\\nhost=example.com\\nprotocol=https\\n\" |\ngit credential-cache get\n# query without capability authtype (expect nothing)\nprintf \"host=example.com\\nprotocol=https\\n\" | git credential-cache get\n\nIf you agree that this is a bug, we could add a test case to\nhelper_test_authtype.\n\nHere's a second simpler example of credential-cache of upgrading a request:\n\ngit credential-cache exit\n# store credential\nprintf \"host=example.com\\nprotocol=https\\nusername=tim\\npassword=hunter2\\n\"\n| git credential-cache store\n# get credential (response is upgraded with capability authtype)\nprintf \"host=example.com\\nprotocol=https\" | git credential-cache get\n\n\n\n>\n> Hopefully this is helpful.\n> --\n> brian m. carlson (they/them or he/him)\n> Toronto, Ontario, CA\n"},{"id":"509461","messageId":"CAGJzqskPyjhLQKP7DmfE8kYF9NHTPqQ35eG9nbOPGg9AD+oLXQ@mail.gmail.com","threadId":"62668","inReplyTo":"CAGJzqsmQY9p+nxHPB67N1P4i361adMbvT-BNR7UKfsSY=QGi2A@mail.gmail.com","subject":"Re: Fwd: [Bug] `credential fill` prints incomplete bearer credential","fromName":"M Hickford","fromEmail":"mirth.hickford@gmail.com","sentAt":"2024-12-21T08:08:29Z","receivedAt":"2024-12-21T08:09:12Z","isPatch":false,"sender":{"key":"mirth.hickford@gmail.com","avatar":"https://avatars.githubusercontent.com/u/105314?v=4"},"body":"On Thu, 19 Dec 2024 at 19:15, M Hickford <mirth.hickford@gmail.com> wrote:\n>\n> On Thu, 19 Dec 2024 at 02:02, brian m. carlson\n> <sandals@crustytoothpaste.net> wrote:\n> >\n> > On 2024-12-18 at 20:42:31, M Hickford wrote:\n> > > Hi. Is this a bug in git version 2.47.1? Or am I using it incorrectly?\n> > >\n> > > # erase existing example.com credentials\n> > > printf \"host=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential reject\n> > > # store bearer token with expiry in far future in credential-cache\n> > > printf \"host=example.com\\nprotocol=https\\nauthtype=bearer\\ncredential=letmein\\npassword_expiry_utc=2147483640\\n\"\n> > > | git credential-cache store\n> > > # try to retrieve credential\n> > > printf \"host=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential fill\n> > >\n> > > Expected output (complete credential):\n> > >\n> > > protocol=https\n> > > host=example.com\n> > > authtype=bearer\n> > > credential=letmein\n> > > password_expiry_utc=2147483640\n> > >\n> > > Actual output (incomplete credential, no prompt for username or password):\n> > >\n> > > protocol=https\n> > > host=example.com\n> > > password_expiry_utc=2147483640\n> >\n> > This is expected.  Every request to a credential helper should include\n> > all of the capabilities that the caller supports on input, and the\n> > credential helper will always emit those on output.  `git credential`,\n> > however, will only emit the capabilities that were actually supported,\n> > so that general callers (including Git LFS) can determine the actual\n> > set of supported capabilities.\n> >\n> > In this case, you asked the cache helper for a credential, but didn't\n> > tell it that you supported `authtype` and `credential`.  Therefore, the\n> > only safe thing it can assume is that you are incapable of parsing and\n> > understanding those fields, so it doesn't emit them.  This is a benefit\n> > for security, because some tooling logs all fields but the `password`\n> > field, and we don't want to include new secret fields that the caller is\n> > going to shovel into a file or syslog.\n> >\n> > In addition, the helper could actually store two different sets of\n> > credentials, one which is a username and password, and one which is an\n> > authtype and credential.  If you provided the capability, the latter\n> > would be omitted, but otherwise the former would.  That can be helpful\n> > if you have a stronger credential type but might occasionally need to\n> > use older software (say, older versions of Git or Git LFS).\n> >\n> > However, if you provide the proper capability, this works as you expect:\n> >\n> > ----\n> > % printf \"host=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential reject\n> > % printf \"capability[]=authtype\\nhost=example.com\\nprotocol=https\\nauthtype=bearer\\ncredential=letmein\\npassword_expiry_utc=2147483640\\n\" | git credential-cache store\n> > % printf \"capability[]=authtype\\nhost=example.com\\nprotocol=https\\n\" | git -c credential.helper= -c credential.helper=cache credential fill\n> > capability[]=authtype\n> > authtype=bearer\n> > credential=letmein\n> > protocol=https\n> > host=example.com\n> > password_expiry_utc=2147483640\n> > ----\n> >\n> > Note that `capability[]` directives should always start the request to\n> > allow one-pass parsing.\n>\n> I think a bug exists in credential-cache. Below it receives a query\n> *without* capability authtype, upgrades it *with* capability authtype\n> and prints a bearer credential.\n>\n> git credential-cache exit\n> # store bearer credential\n> printf \"capability[]=authtype\\nhost=example.com\\nprotocol=https\\nauthtype=bearer\\ncredential=letmein\\npassword_expiry_utc=2147483640\\n\"\n> | git credential-cache store\n> # query with capability authtype (prints bearer credential as expected)\n> printf \"capability[]=authtype\\nhost=example.com\\nprotocol=https\\n\" |\n> git credential-cache get\n> # query without capability authtype (expect nothing)\n> printf \"host=example.com\\nprotocol=https\\n\" | git credential-cache get\n>\n> If you agree that this is a bug, we could add a test case to\n> helper_test_authtype.\n\nHere's a small fix and test for credential-cache\nhttps://lore.kernel.org/git/pull.1842.git.1734729534213.gitgitgadget@gmail.com/\n\n>\n> Here's a second simpler example of credential-cache of upgrading a request:\n>\n> git credential-cache exit\n> # store credential\n> printf \"host=example.com\\nprotocol=https\\nusername=tim\\npassword=hunter2\\n\"\n> | git credential-cache store\n> # get credential (response is upgraded with capability authtype)\n> printf \"host=example.com\\nprotocol=https\" | git credential-cache get\n>\n>\n>\n> >\n> > Hopefully this is helpful.\n> > --\n> > brian m. carlson (they/them or he/him)\n> > Toronto, Ontario, CA\n"}]}