v1_git_credential.py
python
sha256:33412f8ca4d4d6c3b72e9f6a6097ef8395b863f35f7451bf475a3cc0fb98ce9c
docs: restore exact original site presentation
Human
7 days ago
| 1 | """Ephemeral Git credential helper: one approved GitHub HTTPS repository only. |
| 2 | |
| 3 | Invoked by isolated Git, never as a user-facing token command. Store/erase are |
| 4 | deliberate no-ops. gh output is held in memory and returned only on Git's pipe. |
| 5 | """ |
| 6 | from pathlib import Path |
| 7 | import os |
| 8 | import re |
| 9 | import subprocess |
| 10 | import sys |
| 11 | |
| 12 | |
| 13 | def main(): |
| 14 | if len(sys.argv) != 4: |
| 15 | return 1 |
| 16 | gh, repository, operation = sys.argv[1:] |
| 17 | if operation in ('store', 'erase'): |
| 18 | return 0 |
| 19 | path = Path(gh) |
| 20 | if (operation != 'get' or not path.is_absolute() or path.resolve() != path |
| 21 | or any(p.is_symlink() for p in (path, *path.parents)) |
| 22 | or not path.is_file() or not os.access(path, os.X_OK) |
| 23 | or not re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9_.-]*/[A-Za-z0-9][A-Za-z0-9_.-]*', repository)): |
| 24 | return 1 |
| 25 | raw = sys.stdin.buffer.read(16385) |
| 26 | if len(raw) > 16384: |
| 27 | return 1 |
| 28 | fields = {} |
| 29 | for row in raw.decode('utf-8', errors='strict').split('\n'): |
| 30 | if not row: |
| 31 | continue |
| 32 | if any(ord(c) < 32 or ord(c) == 127 for c in row): |
| 33 | return 1 |
| 34 | key, sep, value = row.partition('=') |
| 35 | if not sep: |
| 36 | return 1 |
| 37 | # Git sends repeatable capability offers and HTTP authentication challenges. |
| 38 | # Ignore only these metadata arrays: they cannot change destination scope, |
| 39 | # select a credential type, or opt this username/password helper into state. |
| 40 | # The whole request remains bounded; unknown fields still fail closed. |
| 41 | if key in ('capability[]', 'wwwauth[]'): |
| 42 | continue |
| 43 | if key in fields or key not in ('protocol', 'host', 'path', 'username'): |
| 44 | return 1 |
| 45 | fields[key] = value |
| 46 | if any(fields.get(k) != v for k, v in |
| 47 | {'protocol': 'https', 'host': 'github.com', 'path': repository + '.git'}.items()): |
| 48 | return 1 |
| 49 | environment = {k: v for k, v in os.environ.items() if k not in ('GH_DEBUG', 'GH_HOST')} |
| 50 | environment.update(GH_HOST='github.com', GH_PROMPT_DISABLED='1') |
| 51 | proc = subprocess.run([gh, 'auth', 'token', '--hostname', 'github.com'], |
| 52 | env=environment, capture_output=True, timeout=15) |
| 53 | token = proc.stdout.strip() |
| 54 | if proc.returncode or not token or len(token) > 4096 or any(c < 33 or c > 126 for c in token): |
| 55 | return 1 |
| 56 | sys.stdout.buffer.write(b'username=x-access-token\npassword=' + token + b'\n\n') |
| 57 | return 0 |
| 58 | |
| 59 | |
| 60 | if __name__ == '__main__': |
| 61 | try: |
| 62 | raise SystemExit(main()) |
| 63 | except (OSError, ValueError, subprocess.SubprocessError): |
| 64 | # Never echo credential subprocess output or input fields on failure. |
| 65 | raise SystemExit(1) from None |
File History
1 commit
sha256:33412f8ca4d4d6c3b72e9f6a6097ef8395b863f35f7451bf475a3cc0fb98ce9c
docs: restore exact original site presentation
Human
7 days ago