Skip to content

Commit 10e62dc

Browse files
committed
fix(manager): redact secrets from job responses
1 parent c9e2e6d commit 10e62dc

3 files changed

Lines changed: 143 additions & 11 deletions

File tree

manager/handlers/job.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func (h *Handlers) CreateJob(ctx *gin.Context) {
6363
return
6464
}
6565

66-
ctx.JSON(http.StatusOK, job)
66+
ctx.JSON(http.StatusOK, sanitizeJobForResponse(job))
6767
case job.SyncPeersJob:
6868
var json types.CreateSyncPeersJobRequest
6969
if err := ctx.ShouldBindBodyWith(&json, binding.JSON); err != nil {
@@ -98,7 +98,7 @@ func (h *Handlers) CreateJob(ctx *gin.Context) {
9898
return
9999
}
100100

101-
ctx.JSON(http.StatusOK, job)
101+
ctx.JSON(http.StatusOK, sanitizeJobForResponse(job))
102102
case job.GetImageDistributionJob:
103103
var json types.CreateGetImageDistributionJobRequest
104104
if err := ctx.ShouldBindBodyWith(&json, binding.JSON); err != nil {
@@ -131,7 +131,7 @@ func (h *Handlers) CreateJob(ctx *gin.Context) {
131131
return
132132
}
133133

134-
ctx.JSON(http.StatusOK, job)
134+
ctx.JSON(http.StatusOK, sanitizeJobForResponse(job))
135135
case job.GCJob:
136136
var json types.CreateGCJobRequest
137137
if err := ctx.ShouldBindBodyWith(&json, binding.JSON); err != nil {
@@ -145,7 +145,7 @@ func (h *Handlers) CreateJob(ctx *gin.Context) {
145145
return
146146
}
147147

148-
ctx.JSON(http.StatusOK, job)
148+
ctx.JSON(http.StatusOK, sanitizeJobForResponse(job))
149149
default:
150150
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": "Unknow type"})
151151
}
@@ -210,7 +210,7 @@ func (h *Handlers) UpdateJob(ctx *gin.Context) {
210210
return
211211
}
212212

213-
ctx.JSON(http.StatusOK, job)
213+
ctx.JSON(http.StatusOK, sanitizeJobForResponse(job))
214214
}
215215

216216
// @Summary Get Job
@@ -238,7 +238,7 @@ func (h *Handlers) GetJob(ctx *gin.Context) {
238238
return
239239
}
240240

241-
ctx.JSON(http.StatusOK, job)
241+
ctx.JSON(http.StatusOK, sanitizeJobForResponse(job))
242242
}
243243

244244
// @Summary Get Jobs
@@ -269,5 +269,5 @@ func (h *Handlers) GetJobs(ctx *gin.Context) {
269269
}
270270

271271
h.setPaginationLinkHeader(ctx, query.Page, query.PerPage, int(count))
272-
ctx.JSON(http.StatusOK, jobs)
272+
ctx.JSON(http.StatusOK, sanitizeJobsForResponse(jobs))
273273
}

manager/handlers/job_response.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright 2026 The Dragonfly Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package handlers
18+
19+
import "d7y.io/dragonfly/v2/manager/models"
20+
21+
var (
22+
jobArgsSecretKeys = map[string]struct{}{
23+
"password": {},
24+
"headers": {},
25+
}
26+
27+
objectStorageSecretKeys = map[string]struct{}{
28+
"access_key_id": {},
29+
"access_key_secret": {},
30+
"session_token": {},
31+
"security_token": {},
32+
}
33+
34+
hdfsSecretKeys = map[string]struct{}{
35+
"delegation_token": {},
36+
}
37+
)
38+
39+
func sanitizeJobForResponse(job *models.Job) models.Job {
40+
if job == nil {
41+
return models.Job{}
42+
}
43+
44+
sanitized := *job
45+
sanitized.Args = sanitizeJobArgs(job.Args)
46+
return sanitized
47+
}
48+
49+
func sanitizeJobsForResponse(jobs []models.Job) []models.Job {
50+
sanitized := make([]models.Job, 0, len(jobs))
51+
for i := range jobs {
52+
sanitized = append(sanitized, sanitizeJobForResponse(&jobs[i]))
53+
}
54+
55+
return sanitized
56+
}
57+
58+
func sanitizeJobArgs(args models.JSONMap) models.JSONMap {
59+
if args == nil {
60+
return nil
61+
}
62+
63+
sanitized := make(models.JSONMap, len(args))
64+
for key, value := range args {
65+
if _, ok := jobArgsSecretKeys[key]; ok {
66+
continue
67+
}
68+
69+
switch key {
70+
case "object_storage":
71+
sanitized[key] = sanitizeNestedMap(value, objectStorageSecretKeys)
72+
case "hdfs":
73+
sanitized[key] = sanitizeNestedMap(value, hdfsSecretKeys)
74+
default:
75+
sanitized[key] = value
76+
}
77+
}
78+
79+
return sanitized
80+
}
81+
82+
func sanitizeNestedMap(value any, secretKeys map[string]struct{}) any {
83+
nested, ok := value.(map[string]any)
84+
if !ok {
85+
return value
86+
}
87+
88+
sanitized := make(map[string]any, len(nested))
89+
for key, nestedValue := range nested {
90+
if _, ok := secretKeys[key]; ok {
91+
continue
92+
}
93+
94+
sanitized[key] = nestedValue
95+
}
96+
97+
return sanitized
98+
}

manager/handlers/job_test.go

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,40 @@ var (
9292
Type: "preheat",
9393
BIO: "bio",
9494
TaskID: "dec6fe878785cea844dcecdf2ea25e19156822201016455733e47e9f0bfab563",
95+
Args: models.JSONMap{
96+
"url": "https://registry.example.com/v2/library/busybox/manifests/latest",
97+
"username": "robot$dragonfly",
98+
"password": "super-secret",
99+
"headers": map[string]any{
100+
"Authorization": "Bearer abc123",
101+
"Accept": "application/json",
102+
},
103+
"object_storage": map[string]any{
104+
"endpoint": "https://s3.example.com",
105+
"access_key_id": "access-key",
106+
"access_key_secret": "secret-key",
107+
"session_token": "session-token",
108+
"security_token": "security-token",
109+
},
110+
"hdfs": map[string]any{
111+
"delegation_token": "delegation-token",
112+
},
113+
},
114+
}
115+
mockSanitizedPreheatJobModel = &models.Job{
116+
BaseModel: mockBaseModel,
117+
UserID: 4,
118+
Type: "preheat",
119+
BIO: "bio",
120+
TaskID: "dec6fe878785cea844dcecdf2ea25e19156822201016455733e47e9f0bfab563",
121+
Args: models.JSONMap{
122+
"url": "https://registry.example.com/v2/library/busybox/manifests/latest",
123+
"username": "robot$dragonfly",
124+
"object_storage": map[string]any{
125+
"endpoint": "https://s3.example.com",
126+
},
127+
"hdfs": map[string]any{},
128+
},
95129
}
96130
mockGetTaskJobModel = &models.Job{
97131
BaseModel: mockBaseModel,
@@ -158,7 +192,7 @@ func TestHandlers_CreateJob(t *testing.T) {
158192
job := models.Job{}
159193
err := json.Unmarshal(w.Body.Bytes(), &job)
160194
assert.NoError(err)
161-
assert.Equal(mockPreheatJobModel, &job)
195+
assert.Equal(mockSanitizedPreheatJobModel, &job)
162196
},
163197
},
164198
{
@@ -289,7 +323,7 @@ func TestHandlers_UpdateJob(t *testing.T) {
289323
job := models.Job{}
290324
err := json.Unmarshal(w.Body.Bytes(), &job)
291325
assert.NoError(err)
292-
assert.Equal(mockPreheatJobModel, &job)
326+
assert.Equal(mockSanitizedPreheatJobModel, &job)
293327
},
294328
},
295329
}
@@ -337,7 +371,7 @@ func TestHandlers_GetJob(t *testing.T) {
337371
job := models.Job{}
338372
err := json.Unmarshal(w.Body.Bytes(), &job)
339373
assert.NoError(err)
340-
assert.Equal(mockPreheatJobModel, &job)
374+
assert.Equal(mockSanitizedPreheatJobModel, &job)
341375
},
342376
},
343377
}
@@ -389,7 +423,7 @@ func TestHandlers_GetJobs(t *testing.T) {
389423
job := models.Job{}
390424
err := json.Unmarshal(w.Body.Bytes()[1:w.Body.Len()-1], &job)
391425
assert.NoError(err)
392-
assert.Equal(mockPreheatJobModel, &job)
426+
assert.Equal(mockSanitizedPreheatJobModel, &job)
393427
},
394428
},
395429
}

0 commit comments

Comments
 (0)