You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
GitHub App installation token is written to build logs when an app uses private submodules #5618
When an application's source is the GitHub provider with Enable Submodules on, and the repository's .gitmodules uses relative URLs (url = ../other-repo.git, the usual way to make private submodules clone), every build log contains the GitHub App installation token in clear text:
Submodule 'third_party/example' (https://oauth2:ghs_…@github.com/<owner>/example.git) registered for path …
One line per submodule, every build.
Why
packages/server/src/utils/providers/github.ts puts the token into the clone URL (v0.29.8 line 167; still on main at line 232 as of 2026-10-08):
and clones with --recurse-submodules. Git resolves each relative submodule URL against that remote URL, so the submodules inherit the token (which is what makes private submodules work at all), and git prints each resolved URL as it registers the submodule.
Impact
The logs live under /etc/dokploy/logs/<app>/ (mode 644 on my install) and are shown in the Dokploy UI, including the live stream during a build.
An installation token is short-lived, about an hour, but while it lives it carries the installation's access: every repository the GitHub App is installed on, not only the one being built.
Related: #4600 covers other secrets reaching the logs through shell interpolation. This one is the GitHub token, reaching the log through git's own output rather than through a command line, so a command-level redaction would not catch it.
Suggested fix (not tested here)
Keep the token out of the URL so nothing derived from it is ever printed. One way is a header for the clone and everything it spawns:
(-c settings reach the submodule clones through GIT_CONFIG_PARAMETERS.) A credential helper or GIT_ASKPASS would work as well. As defence in depth, the deployment log writer could redact //oauth2:[^@]+@.
Workaround in use meanwhile
A root timer that redacts https://oauth2:<token>@ in finished build logs, touching only files idle for a couple of minutes, because rewriting a log that is still being written truncates it.
Environment
dokploy/dokploy v0.29.8, self-hosted on Ubuntu 24.04 with Docker Swarm; GitHub provider through a GitHub App; application build type Dockerfile with Enable Submodules on. The line is unchanged on main as of 2026-10-08 (latest release v0.30.8).
I'd like to work on this. Reproduced it locally: the token reaches the log through the submodule URL that git resolves from the clone URL. Plan is to keep the token out of the URL and pass it as a host-scoped http.<url>.extraHeader (the same Authorization: Basic header git already sends), which also keeps it out of .git/config. Tested with a private relative submodule: clone works and the token no longer appears in the output.
Activity
linear-code commentedon Oct 8, 2026
DOK-843
Souvik-Cyclic commentedon Oct 8, 2026
I'd like to work on this. Reproduced it locally: the token reaches the log through the submodule URL that git resolves from the clone URL. Plan is to keep the token out of the URL and pass it as a host-scoped
http.<url>.extraHeader(the sameAuthorization: Basicheader git already sends), which also keeps it out of.git/config. Tested with a private relative submodule: clone works and the token no longer appears in the output.Souvik-Cyclic commentedon Oct 8, 2026
Opened #5619 to fix this. It also covers GitLab and Gitea, which build the clone URL the same way.