Skip to content

Commit 5e2075d

Browse files
authored
Fix GL Runner Exploit to skip credential validation in dry-run mode (#468)
* Fix GL Runner Exploit flag validation for dry-run mode
1 parent 8260d28 commit 5e2075d

2 files changed

Lines changed: 74 additions & 15 deletions

File tree

internal/cmd/gitlab/runners/exploit/exploit.go

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,17 @@ func NewRunnersExploitCmd() *cobra.Command {
2222
# Creates a project with jobs for all available runners with the tags docker, shared. Dumps the envs encrypted using Age and starts an interactive SSHX shell,
2323
pipeleek gl runners exploit --token glpat-xxxxxxxxxxx --gitlab https://gitlab.mydomain.com --tags docker,shared --age-public-key age1... --repo-name my-exploit-repo --dry=false --shell=true
2424
25-
# Print the generated .gitlab-ci.yml only, does NOT create a project or jobs
26-
pipeleek gl runners exploit --token glpat-xxxxxxxxxxx --gitlab https://gitlab.mydomain.com --dry=true --shell=true
25+
# Print the generated .gitlab-ci.yml only, does NOT create a project or jobs (gitlab and token flags not required)
26+
pipeleek gl runners exploit --dry=true --shell=true
2727
`,
2828
Run: func(cmd *cobra.Command, args []string) {
29-
if err := config.BindCommandFlags(cmd, "gitlab.runners.exploit", nil); err != nil {
29+
if err := config.BindCommandFlags(cmd, "gitlab.runners.exploit", map[string]string{
30+
"gitlab": "gitlab.url",
31+
"token": "gitlab.token",
32+
}); err != nil {
3033
log.Fatal().Err(err).Msg("Failed to bind flags to config")
3134
}
3235

33-
// Get gitlab URL and token from parent's vars (set by parent PersistentPreRun)
34-
gitlabUrl, _ := cmd.Flags().GetString("gitlab")
35-
gitlabApiToken, _ := cmd.Flags().GetString("token")
36-
3736
// Get values from config (supports CLI flags, config file, and env vars)
3837
if !cmd.Flags().Changed("tags") {
3938
runnerTags = config.GetStringSlice("gitlab.runners.exploit.tags")
@@ -51,7 +50,28 @@ pipeleek gl runners exploit --token glpat-xxxxxxxxxxx --gitlab https://gitlab.my
5150
shell = config.GetBool("gitlab.runners.exploit.shell")
5251
}
5352

54-
pkgrunners.ExploitRunners(runnerTags, dry, shell, gitlabApiToken, gitlabUrl, ageEncryptionPublicKey, repoName)
53+
// Only require gitlab URL and token when not in dry-run mode
54+
if !dry {
55+
if err := config.RequireConfigKeys("gitlab.url", "gitlab.token"); err != nil {
56+
log.Fatal().Err(err).Msg("Missing required configuration")
57+
}
58+
59+
gitlabUrl := config.GetString("gitlab.url")
60+
gitlabApiToken := config.GetString("gitlab.token")
61+
62+
if err := config.ValidateURL(gitlabUrl, "GitLab URL"); err != nil {
63+
log.Fatal().Err(err).Msg("Invalid GitLab URL")
64+
}
65+
if err := config.ValidateToken(gitlabApiToken, "GitLab API Token"); err != nil {
66+
log.Fatal().Err(err).Msg("Invalid GitLab API Token")
67+
}
68+
69+
pkgrunners.ExploitRunners(runnerTags, dry, shell, gitlabApiToken, gitlabUrl, ageEncryptionPublicKey, repoName)
70+
} else {
71+
// In dry-run mode, we don't need gitlab URL and token
72+
pkgrunners.ExploitRunners(runnerTags, dry, shell, "", "", ageEncryptionPublicKey, repoName)
73+
}
74+
5575
log.Info().Msg("Done, Bye Bye 🏳️‍🌈🔥")
5676
},
5777
}

tests/e2e/gitlab/runners/runners_test.go

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ func setupMockGitLabRunnersAPI(t *testing.T) string {
2626
w.Write([]byte(`[{"id":123,"name":"test-project"}]`))
2727
})
2828

29+
// Repository files endpoint for creating .gitlab-ci.yml
30+
mux.HandleFunc("/api/v4/projects/999/repository/files/.gitlab-ci.yml", func(w http.ResponseWriter, r *http.Request) {
31+
if r.Method == http.MethodPost {
32+
w.WriteHeader(http.StatusCreated)
33+
w.Write([]byte(`{"file_path":".gitlab-ci.yml","branch":"main"}`))
34+
return
35+
}
36+
w.WriteHeader(http.StatusOK)
37+
})
38+
2939
// Project runners endpoint
3040
mux.HandleFunc("/api/v4/projects/123/runners", func(w http.ResponseWriter, r *http.Request) {
3141
w.WriteHeader(http.StatusOK)
@@ -105,7 +115,7 @@ func TestGLRunnersExploit_DryRun(t *testing.T) {
105115
"--gitlab", apiURL,
106116
"--token", "mock-token",
107117
"--tags", "docker,shell",
108-
"--dry", "true",
118+
"--dry=true",
109119
}, nil, 10*time.Second)
110120

111121
assert.Nil(t, exitErr, "Runners exploit dry run should succeed")
@@ -114,6 +124,33 @@ func TestGLRunnersExploit_DryRun(t *testing.T) {
114124
assert.NotContains(t, stderr, "fatal")
115125
}
116126

127+
func TestGLRunnersExploit_DryRun_WithoutTokenAndGitlab(t *testing.T) {
128+
stdout, stderr, exitErr := testutil.RunCLI(t, []string{
129+
"gl", "runners", "exploit",
130+
"--dry=true",
131+
"--tags", "docker,shell",
132+
}, nil, 10*time.Second)
133+
134+
assert.Nil(t, exitErr, "Runners exploit dry run should succeed without token and gitlab flags")
135+
assert.Contains(t, stdout, ".gitlab-ci.yml", "Should show CI/CD YAML output")
136+
assert.Contains(t, stdout, "docker", "Should include docker tag in YAML")
137+
assert.NotContains(t, stderr, "fatal")
138+
}
139+
140+
func TestGLRunnersExploit_NonDryRun_WithoutTokenAndGitlab(t *testing.T) {
141+
stdout, stderr, exitErr := testutil.RunCLI(t, []string{
142+
"gl", "runners", "exploit",
143+
"--dry=false",
144+
}, nil, 10*time.Second)
145+
146+
assert.NotNil(t, exitErr, "Should fail without token and gitlab flags when not in dry-run mode")
147+
output := stdout + stderr
148+
assert.Contains(t, output, "required configuration missing", "Should mention missing required configuration")
149+
assert.Contains(t, output, "gitlab.url", "Should mention gitlab.url is missing")
150+
assert.Contains(t, output, "gitlab.token", "Should mention gitlab.token is missing")
151+
}
152+
153+
117154
func TestGLRunnersExploit_WithRepoCreation(t *testing.T) {
118155
apiURL := setupMockGitLabRunnersAPI(t)
119156
stdout, stderr, exitErr := testutil.RunCLI(t, []string{
@@ -122,8 +159,8 @@ func TestGLRunnersExploit_WithRepoCreation(t *testing.T) {
122159
"--token", "mock-token",
123160
"--tags", "docker",
124161
"--repo-name", "test-exploit-repo",
125-
"--dry", "false",
126-
"--shell", "false",
162+
"--dry=false",
163+
"--shell=false",
127164
}, nil, 15*time.Second)
128165

129166
assert.Nil(t, exitErr, "Runners exploit should succeed")
@@ -140,13 +177,15 @@ func TestGLRunnersExploit_Unauthorized(t *testing.T) {
140177
server := httptest.NewServer(mux)
141178
defer server.Close()
142179

143-
stdout, _, _ := testutil.RunCLI(t, []string{
180+
stdout, stderr, exitErr := testutil.RunCLI(t, []string{
144181
"gl", "runners", "exploit",
145182
"--gitlab", server.URL,
146183
"--token", "invalid-token",
147-
"--dry", "false",
184+
"--dry=false",
148185
}, nil, 10*time.Second)
149186

150-
// Exploit command generates YAML in dry mode regardless
151-
assert.Contains(t, stdout, "Done, Bye Bye", "Should complete with generated YAML")
187+
// Should fail with unauthorized error when creating project
188+
assert.NotNil(t, exitErr, "Should fail with unauthorized error")
189+
output := stdout + stderr
190+
assert.Contains(t, output, "401", "Should mention 401 error")
152191
}

0 commit comments

Comments
 (0)