Skip to content

Commit fedc25e

Browse files
authored
fix(logging): reduce verbose logging for security. Fixes #12293 (#12295)
add back additionalinfo as optional make k8s error logs more generic to protect against xss remove verbose logging for minio get artifact handler sanitize rather than remove resource names + revert backend error logs fix typo + add invalid resource name tests add valid namespace check for tensorboard update pipeline upload server responses + log verbose error messages remove error wrap from backend error logs minor updates to satisfy pre commit checks update tensor board file to ensure namespace is a str update frontend code format + backend test cases update pipeline upload test cases to pass pre commit fix minor merge typo fix minor merge typo Signed-off-by: JerT33 <[email protected]>
1 parent 658da50 commit fedc25e

File tree

7 files changed

+145
-63
lines changed

7 files changed

+145
-63
lines changed

backend/src/apiserver/server/pipeline_upload_server.go

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
/*
16+
Package server contains the implementation of the pipeline upload server.
17+
*/
1518
package server
1619

1720
import (
@@ -65,7 +68,7 @@ var (
6568

6669
type PipelineUploadServerOptions struct {
6770
CollectMetrics bool `json:"collect_metrics,omitempty"`
68-
// ApiVersion string `default:"v2beta1" json:"api_version,omitempty"`
71+
// ApiVersion string `default:"v2beta1" json:"apiVersion,omitempty"`
6972
// DefaultNamespace string `default:"" json:"default_namespace,omitempty"`
7073
}
7174

@@ -89,7 +92,7 @@ func (s *PipelineUploadServer) UploadPipeline(w http.ResponseWriter, r *http.Req
8992
// endpoint to the HTTP endpoint.
9093
// See https://github.com/grpc-ecosystem/grpc-gateway/issues/500
9194
// Thus we create the HTTP endpoint directly and using swagger to auto generate the HTTP client.
92-
func (s *PipelineUploadServer) uploadPipeline(api_version string, w http.ResponseWriter, r *http.Request) {
95+
func (s *PipelineUploadServer) uploadPipeline(apiVersion string, w http.ResponseWriter, r *http.Request) {
9396
if s.options.CollectMetrics {
9497
uploadPipelineRequests.Inc()
9598
uploadPipelineVersionRequests.Inc()
@@ -98,14 +101,16 @@ func (s *PipelineUploadServer) uploadPipeline(api_version string, w http.Respons
98101
glog.Infof("Upload pipeline called")
99102
file, header, err := r.FormFile(FormFileKey)
100103
if err != nil {
101-
s.writeErrorToResponse(w, http.StatusBadRequest, util.Wrap(err, "Failed to read pipeline from file"))
104+
glog.Errorf("Failed to read pipeline from file: %v", err)
105+
s.writeErrorToResponse(w, http.StatusBadRequest, errors.New("Failed to read pipeline from file"))
102106
return
103107
}
104108
defer file.Close()
105109

106110
pipelineFile, err := ReadPipelineFile(header.Filename, file, common.MaxFileLength)
107111
if err != nil {
108-
s.writeErrorToResponse(w, http.StatusBadRequest, util.Wrap(err, "Failed to read a pipeline spec file"))
112+
glog.Errorf("Failed to read a pipeline spec file: %v", err)
113+
s.writeErrorToResponse(w, http.StatusBadRequest, errors.New("Failed to read a pipeline spec file"))
109114
return
110115
}
111116

@@ -126,7 +131,8 @@ func (s *PipelineUploadServer) uploadPipeline(api_version string, w http.Respons
126131
}
127132
err = s.canUploadVersionedPipeline(r, "", resourceAttributes)
128133
if err != nil {
129-
s.writeErrorToResponse(w, http.StatusBadRequest, util.Wrap(err, "Failed to create a pipeline due to authorization error"))
134+
glog.Errorf("Failed to create a pipeline due to authorization error: %v", err)
135+
s.writeErrorToResponse(w, http.StatusBadRequest, errors.New("Failed to create a pipeline"))
130136
return
131137
}
132138

@@ -162,10 +168,12 @@ func (s *PipelineUploadServer) uploadPipeline(api_version string, w http.Respons
162168
newPipeline, newPipelineVersion, err := s.resourceManager.CreatePipelineAndPipelineVersion(pipeline, pipelineVersion)
163169
if err != nil {
164170
if util.IsUserErrorCodeMatch(err, codes.AlreadyExists) {
165-
s.writeErrorToResponse(w, http.StatusConflict, util.Wrap(err, "Failed to create a pipeline and a pipeline version. The pipeline already exists."))
171+
glog.Errorf("Failed to create a pipeline and a pipeline version. The pipeline already exists: %v", err)
172+
s.writeErrorToResponse(w, http.StatusConflict, errors.New("Failed to create a pipeline and a pipeline version"))
166173
return
167174
}
168-
s.writeErrorToResponse(w, http.StatusInternalServerError, util.Wrap(err, "Failed to create a pipeline and a pipeline version"))
175+
glog.Errorf("Failed to create a pipeline and a pipeline version: %v", err)
176+
s.writeErrorToResponse(w, http.StatusInternalServerError, errors.New("Failed to create a pipeline and a pipeline version"))
169177
return
170178
}
171179

@@ -174,13 +182,14 @@ func (s *PipelineUploadServer) uploadPipeline(api_version string, w http.Respons
174182
}
175183

176184
var messageToMarshal proto.Message
177-
switch api_version {
185+
switch apiVersion {
178186
case "v1beta1":
179187
messageToMarshal = toApiPipelineV1(newPipeline, newPipelineVersion)
180188
case "v2beta1":
181189
messageToMarshal = toApiPipeline(newPipeline)
182190
default:
183-
s.writeErrorToResponse(w, http.StatusInternalServerError, util.Wrap(err, "Failed to create a pipeline. Invalid API version"))
191+
glog.Errorf("Failed to create a pipeline. Invalid API version: %v", apiVersion)
192+
s.writeErrorToResponse(w, http.StatusInternalServerError, errors.New("Failed to create a pipeline"))
184193
return
185194
}
186195

@@ -191,12 +200,14 @@ func (s *PipelineUploadServer) uploadPipeline(api_version string, w http.Respons
191200
}
192201
data, err := marshaler.Marshal(messageToMarshal)
193202
if err != nil {
194-
s.writeErrorToResponse(w, http.StatusInternalServerError, util.Wrap(err, "Failed to create a pipeline. Marshaling error"))
203+
glog.Errorf("Failed to create a pipeline. Marshaling error: %v", err)
204+
s.writeErrorToResponse(w, http.StatusInternalServerError, errors.New("Failed to create a pipeline"))
195205
return
196206
}
197207
_, err = w.Write(data)
198208
if err != nil {
199-
s.writeErrorToResponse(w, http.StatusInternalServerError, util.Wrap(err, "Failed to create a pipeline. Write error."))
209+
glog.Errorf("Failed to create a pipeline. Write error: %v", err)
210+
s.writeErrorToResponse(w, http.StatusInternalServerError, errors.New("Failed to create a pipeline"))
200211
return
201212
}
202213
}
@@ -216,27 +227,30 @@ func (s *PipelineUploadServer) UploadPipelineVersion(w http.ResponseWriter, r *h
216227
// endpoint to the HTTP endpoint.
217228
// See https://github.com/grpc-ecosystem/grpc-gateway/issues/500
218229
// Thus we create the HTTP endpoint directly and using swagger to auto generate the HTTP client.
219-
func (s *PipelineUploadServer) uploadPipelineVersion(api_version string, w http.ResponseWriter, r *http.Request) {
230+
func (s *PipelineUploadServer) uploadPipelineVersion(apiVersion string, w http.ResponseWriter, r *http.Request) {
220231
if s.options.CollectMetrics {
221232
uploadPipelineVersionRequests.Inc()
222233
}
223234

224235
glog.Infof("Upload pipeline version called")
225236
file, header, err := r.FormFile(FormFileKey)
226237
if err != nil {
227-
s.writeErrorToResponse(w, http.StatusBadRequest, util.Wrap(err, "Failed to create a pipeline version due to error parsing pipeline spec filename"))
238+
glog.Errorf("Failed to create a pipeline version. Error parsing pipeline spec filename: %v", err)
239+
s.writeErrorToResponse(w, http.StatusBadRequest, errors.New("Failed to create a pipeline version"))
228240
return
229241
}
230242
defer file.Close()
231243

232244
pipelineFile, err := ReadPipelineFile(header.Filename, file, common.MaxFileLength)
233245
if err != nil {
234-
s.writeErrorToResponse(w, http.StatusBadRequest, util.Wrap(err, "Failed to create a pipeline version due to error reading pipeline spec file"))
246+
glog.Errorf("Failed to create a pipeline version. Error reading pipeline spec file: %v", err)
247+
s.writeErrorToResponse(w, http.StatusBadRequest, errors.New("Failed to create a pipeline version"))
235248
return
236249
}
237-
pipelineId := r.URL.Query().Get(PipelineKey)
238-
if pipelineId == "" {
239-
s.writeErrorToResponse(w, http.StatusBadRequest, errors.New("Failed to create a pipeline version due to error reading pipeline id"))
250+
pipelineID := r.URL.Query().Get(PipelineKey)
251+
if pipelineID == "" {
252+
glog.Errorf("Failed to create a pipeline version. Error reading pipeline id")
253+
s.writeErrorToResponse(w, http.StatusBadRequest, errors.New("Failed to create a pipeline version"))
240254
return
241255
}
242256

@@ -255,9 +269,10 @@ func (s *PipelineUploadServer) uploadPipelineVersion(api_version string, w http.
255269
return
256270
}
257271

258-
namespace, err := s.resourceManager.FetchNamespaceFromPipelineId(pipelineId)
272+
namespace, err := s.resourceManager.FetchNamespaceFromPipelineId(pipelineID)
259273
if err != nil {
260-
s.writeErrorToResponse(w, http.StatusBadRequest, util.Wrap(err, "Failed to create a pipeline version due to error reading namespace"))
274+
glog.Errorf("Failed to create a pipeline version. Error reading namespace: %v", err)
275+
s.writeErrorToResponse(w, http.StatusBadRequest, errors.New("Failed to create a pipeline version"))
261276
return
262277
}
263278

@@ -271,9 +286,10 @@ func (s *PipelineUploadServer) uploadPipelineVersion(api_version string, w http.
271286
Namespace: namespace,
272287
Verb: common.RbacResourceVerbCreate,
273288
}
274-
err = s.canUploadVersionedPipeline(r, pipelineId, resourceAttributes)
289+
err = s.canUploadVersionedPipeline(r, pipelineID, resourceAttributes)
275290
if err != nil {
276-
s.writeErrorToResponse(w, http.StatusBadRequest, util.Wrap(err, "Failed to create a pipeline version due to authorization error"))
291+
glog.Errorf("Failed to create a pipeline version. Authorization error: %v", err)
292+
s.writeErrorToResponse(w, http.StatusBadRequest, errors.New("Failed to create a pipeline version"))
277293
return
278294
}
279295

@@ -284,27 +300,30 @@ func (s *PipelineUploadServer) uploadPipelineVersion(api_version string, w http.
284300
Name: pipelineVersionName,
285301
DisplayName: displayName,
286302
Description: model.LargeText(r.URL.Query().Get(DescriptionQueryStringKey)),
287-
PipelineId: pipelineId,
303+
PipelineId: pipelineID,
288304
PipelineSpec: model.LargeText(pipelineFile),
289305
},
290306
)
291307
if err != nil {
292308
if util.IsUserErrorCodeMatch(err, codes.AlreadyExists) {
293-
s.writeErrorToResponse(w, http.StatusConflict, util.Wrap(err, "Failed to create a pipeline version. The pipeline already exists."))
309+
glog.Errorf("Failed to create a pipeline version. The pipeline already exists: %v", err)
310+
s.writeErrorToResponse(w, http.StatusConflict, errors.New("Failed to create a pipeline version"))
294311
return
295312
}
296-
s.writeErrorToResponse(w, http.StatusInternalServerError, util.Wrap(err, "Failed to create a pipeline version"))
313+
glog.Errorf("Failed to create a pipeline version: %v", err)
314+
s.writeErrorToResponse(w, http.StatusInternalServerError, errors.New("Failed to create a pipeline version"))
297315
return
298316
}
299317

300318
var messageToMarshal proto.Message
301-
switch api_version {
319+
switch apiVersion {
302320
case "v1beta1":
303321
messageToMarshal = toApiPipelineVersionV1(newPipelineVersion)
304322
case "v2beta1":
305323
messageToMarshal = toApiPipelineVersion(newPipelineVersion)
306324
default:
307-
s.writeErrorToResponse(w, http.StatusInternalServerError, util.Wrap(err, "Failed to create a pipeline version. Invalid API version"))
325+
glog.Errorf("Failed to create a pipeline version. Invalid API version: %v", apiVersion)
326+
s.writeErrorToResponse(w, http.StatusInternalServerError, errors.New("Failed to create a pipeline version"))
308327
return
309328
}
310329
// Marshal the message to bytes
@@ -314,12 +333,14 @@ func (s *PipelineUploadServer) uploadPipelineVersion(api_version string, w http.
314333
}
315334
data, err := marshaler.Marshal(messageToMarshal)
316335
if err != nil {
317-
s.writeErrorToResponse(w, http.StatusInternalServerError, util.Wrap(err, "Failed to create a pipeline version. Marshaling error"))
336+
glog.Errorf("Failed to create a pipeline version. Marshaling error: %v", err)
337+
s.writeErrorToResponse(w, http.StatusInternalServerError, errors.New("Failed to create a pipeline version"))
318338
return
319339
}
320340
_, err = w.Write(data)
321341
if err != nil {
322-
s.writeErrorToResponse(w, http.StatusInternalServerError, util.Wrap(err, "Failed to create a pipeline version. Write error."))
342+
glog.Errorf("Failed to create a pipeline version. Write error: %v", err)
343+
s.writeErrorToResponse(w, http.StatusInternalServerError, errors.New("Failed to create a pipeline version"))
323344
return
324345
}
325346

@@ -328,13 +349,13 @@ func (s *PipelineUploadServer) uploadPipelineVersion(api_version string, w http.
328349
}
329350
}
330351

331-
func (s *PipelineUploadServer) canUploadVersionedPipeline(r *http.Request, pipelineId string, resourceAttributes *authorizationv1.ResourceAttributes) error {
352+
func (s *PipelineUploadServer) canUploadVersionedPipeline(r *http.Request, pipelineID string, resourceAttributes *authorizationv1.ResourceAttributes) error {
332353
if !common.IsMultiUserMode() {
333354
// Skip authorization if not multi-user mode.
334355
return nil
335356
}
336-
if len(pipelineId) > 0 {
337-
namespace, err := s.resourceManager.FetchNamespaceFromPipelineId(pipelineId)
357+
if len(pipelineID) > 0 {
358+
namespace, err := s.resourceManager.FetchNamespaceFromPipelineId(pipelineID)
338359
if err != nil {
339360
return util.Wrap(err, "Failed to authorize with the Pipeline ID")
340361
}

backend/src/apiserver/server/pipeline_upload_server_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,31 +49,31 @@ func TestUploadPipeline(t *testing.T) {
4949
tt := []struct {
5050
name string
5151
spec []byte
52-
api_version string
52+
apiVersion string
5353
}{{
5454
name: "upload argo workflow YAML",
5555
spec: []byte("apiVersion: argoproj.io/v1alpha1\nkind: Workflow"),
56-
api_version: "v1beta1",
56+
apiVersion: "v1beta1",
5757
}, {
5858
name: "upload argo workflow YAML",
5959
spec: []byte("apiVersion: argoproj.io/v1alpha1\nkind: Workflow"),
60-
api_version: "v2beta1",
60+
apiVersion: "v2beta1",
6161
}, {
6262
name: "upload pipeline v2 job in proto yaml",
6363
spec: []byte(v2SpecHelloWorld),
64-
api_version: "v1beta1",
64+
apiVersion: "v1beta1",
6565
}, {
6666
name: "upload pipeline v2 job in proto yaml",
6767
spec: []byte(v2SpecHelloWorld),
68-
api_version: "v2beta1",
68+
apiVersion: "v2beta1",
6969
}}
7070
for _, test := range tt {
7171
t.Run(test.name, func(t *testing.T) {
7272
clientManager, server := setupClientManagerAndServer()
7373
bytesBuffer, writer := setupWriter("")
7474
setWriterWithBuffer("uploadfile", "hello-world.yaml", string(test.spec), writer)
7575
var response *httptest.ResponseRecorder
76-
switch test.api_version {
76+
switch test.apiVersion {
7777
case "v1beta1":
7878
response = uploadPipeline("/apis/v1beta1/pipelines/upload",
7979
bytes.NewReader(bytesBuffer.Bytes()), writer, server.UploadPipelineV1)
@@ -100,7 +100,7 @@ func TestUploadPipeline(t *testing.T) {
100100
assert.Equal(t, "1970-01-01T00:00:01Z", parsedResponse.CreatedAt)
101101

102102
// Verify v1 API returns v1 object while v2 API returns v2 object.
103-
switch test.api_version {
103+
switch test.apiVersion {
104104
case "v1beta1":
105105
assert.Equal(t, "123e4567-e89b-12d3-a456-426655440000", parsedResponse.ID)
106106
assert.Equal(t, "", parsedResponse.PipelineID)
@@ -235,19 +235,19 @@ func TestUploadPipelineV2_NameValidation(t *testing.T) {
235235
name: "invalid - capitalized",
236236
spec: []byte(invalidV2specCapitalized),
237237
wantErr: true,
238-
errMsg: "pipeline's name must contain only lowercase alphanumeric characters or '-' and must start with alphanumeric characters",
238+
errMsg: "Failed to create a pipeline and a pipeline version",
239239
},
240240
{
241241
name: "invalid - dot",
242242
spec: []byte(invalidV2specDot),
243243
wantErr: true,
244-
errMsg: "pipeline's name must contain only lowercase alphanumeric characters or '-' and must start with alphanumeric characters",
244+
errMsg: "Failed to create a pipeline and a pipeline version",
245245
},
246246
{
247247
name: "invalid - too long",
248248
spec: []byte(invalidV2specLong),
249249
wantErr: true,
250-
errMsg: "pipeline's name must contain no more than 128 characters",
250+
errMsg: "Failed to create a pipeline and a pipeline version",
251251
},
252252
}
253253
for _, test := range tt {
@@ -574,7 +574,7 @@ func TestUploadPipelineVersion_GetFromFileError(t *testing.T) {
574574
response = uploadPipeline("/apis/v1beta1/pipelines/upload_version?name="+fakeVersionName+"&pipelineid="+DefaultFakeUUID,
575575
bytes.NewReader(bytesBuffer.Bytes()), writer, server.UploadPipelineVersion)
576576
assert.Equal(t, 400, response.Code)
577-
assert.Contains(t, response.Body.String(), "error parsing pipeline spec filename")
577+
assert.Contains(t, response.Body.String(), "Failed to create a pipeline version")
578578
}
579579

580580
func TestUploadPipelineVersion_NameTooLong(t *testing.T) {

frontend/server/app.test.ts

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -295,11 +295,32 @@ describe('UIServer apis', () => {
295295
const spyError = jest.spyOn(console, 'error').mockImplementation(() => null);
296296
request
297297
.get('/k8s/pod?podname=test-pod&podnamespace=test-ns')
298-
.expect(500, 'Could not get pod test-pod in namespace test-ns: pod not found', () => {
298+
.expect(500, 'Could not get pod test-pod in namespace test-ns', () => {
299299
expect(spyError).toHaveBeenCalledTimes(1);
300300
done();
301301
});
302302
});
303+
304+
it('responds with error when invalid resource name', done => {
305+
const readPodSpy = jest.spyOn(K8S_TEST_EXPORT.k8sV1Client, 'readNamespacedPod');
306+
readPodSpy.mockImplementation(() =>
307+
Promise.reject({
308+
body: {
309+
message: 'pod not found',
310+
code: 404,
311+
},
312+
} as any),
313+
);
314+
const spyError = jest.spyOn(console, 'error').mockImplementation(() => null);
315+
request
316+
.get(
317+
'/k8s/pod?podname=test-pod-name&podnamespace=test-namespace%7d%7dt93g1%3Cscript%3Ealert(1)%3C%2fscript%3Ej66h',
318+
)
319+
.expect(500, 'Invalid resource name', err => {
320+
expect(spyError).toHaveBeenCalledTimes(1);
321+
done(err);
322+
});
323+
});
303324
});
304325

305326
describe('/k8s/pod/events', () => {
@@ -353,14 +374,31 @@ describe('UIServer apis', () => {
353374
const spyError = jest.spyOn(console, 'error').mockImplementation(() => null);
354375
request
355376
.get('/k8s/pod/events?podname=test-pod&podnamespace=test-ns')
356-
.expect(
357-
500,
358-
'Error when listing pod events for pod "test-pod" in "test-ns" namespace: no events',
359-
err => {
360-
expect(spyError).toHaveBeenCalledTimes(1);
361-
done(err);
377+
.expect(500, 'Error when listing pod events for pod test-pod in namespace test-ns', err => {
378+
expect(spyError).toHaveBeenCalledTimes(1);
379+
done(err);
380+
});
381+
});
382+
383+
it('responds with error when invalid resource name', done => {
384+
const listEventSpy = jest.spyOn(K8S_TEST_EXPORT.k8sV1Client, 'listNamespacedEvent');
385+
listEventSpy.mockImplementation(() =>
386+
Promise.reject({
387+
body: {
388+
message: 'no events',
389+
code: 404,
362390
},
363-
);
391+
} as any),
392+
);
393+
const spyError = jest.spyOn(console, 'error').mockImplementation(() => null);
394+
request
395+
.get(
396+
'/k8s/pod/events?podname=test-pod-name&podnamespace=test-namespace%7d%7dt93g1%3Cscript%3Ealert(1)%3C%2fscript%3Ej66h',
397+
)
398+
.expect(500, 'Invalid resource name', err => {
399+
expect(spyError).toHaveBeenCalledTimes(1);
400+
done(err);
401+
});
364402
});
365403
});
366404

0 commit comments

Comments
 (0)