The stub-and-swap proxy is the part I'd steal. My own setup is the lazy version: keys in a .env file that anything running in the project can read, including the agent. Keeping only placeholders in the container and injecting the real token per host removes the "walk away with it" case without changing how any tool is configured.
One question about the header replacement. git over HTTPS sends the token as Basic auth, so the stub ends up base64-encoded inside the Authorization header and a plain string replace wouldn't match it. Does the GitHub addon decode and re-encode that header, or do pushes go through a different path?
Also agree on dev containers: SSH agent forwarding being on by default undoes a lot of the isolation people think they're getting.
Yes, the header is decoded and re-encoded, and pushes go through that same path. The swap itself happens in the generic inject addon (inject.py), not in the github addon:
def _decoded(value: str) -> str:
# git and acli put the token in the password half of Basic auth.
if value.startswith("Basic "):
return base64.b64decode(value[len("Basic ") :]).decode()
return value
def _substitute(value: str, placeholder: str, secret: str) -> str:
if value.startswith("Basic "):
return "Basic " + base64.b64encode(_decoded(value).replace(placeholder, secret).encode()).decode()
return value.replace(placeholder, secret)
The stub-and-swap proxy is the part I'd steal. My own setup is the lazy version: keys in a .env file that anything running in the project can read, including the agent. Keeping only placeholders in the container and injecting the real token per host removes the "walk away with it" case without changing how any tool is configured.
One question about the header replacement. git over HTTPS sends the token as Basic auth, so the stub ends up base64-encoded inside the Authorization header and a plain string replace wouldn't match it. Does the GitHub addon decode and re-encode that header, or do pushes go through a different path?
Also agree on dev containers: SSH agent forwarding being on by default undoes a lot of the isolation people think they're getting.
Yes, the header is decoded and re-encoded, and pushes go through that same path. The swap itself happens in the generic inject addon (inject.py), not in the github addon: