Skip to content
9 changes: 8 additions & 1 deletion server/artifacts/artifact_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,14 @@ func (a *ArtifactServer) returnArtifact(ctx context.Context, w http.ResponseWrit

key, _ := art.GetKey()
w.Header().Add("Content-Disposition", fmt.Sprintf(`filename="%s"`, path.Base(key)))
w.Header().Add("Content-Type", mime.TypeByExtension(path.Ext(key)))
// mime.TypeByExtension relies on the system MIME database, which may not exist in
// minimal container images such as distroless. Fall back to "text/plain; charset=utf-8" so that
// unrecognized extensions (e.g. ".log") are still served with a valid Content-Type.
contentType := mime.TypeByExtension(path.Ext(key))
if contentType == "" {
contentType = "text/plain; charset=utf-8"
}
w.Header().Add("Content-Type", contentType)
a.setSecurityHeaders(w)

_, err = io.Copy(w, stream)
Expand Down
24 changes: 24 additions & 0 deletions server/artifacts/artifact_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ var bucketsOfKeys = map[string][]string{
"my-wf/my-node-1/my-gcs-artifact.tgz",
"my-wf/my-node-1/my-oss-artifact.zip",
"my-wf/my-node-1/my-s3-artifact.tgz",
"my-wf/my-node-1/main.log",
"my-wf/my-node-inline/main.log",
},
"my-bucket-2": {
Expand Down Expand Up @@ -264,6 +265,18 @@ func newServer(t *testing.T) *ArtifactServer {
},
},
},
{
// Log artifact created when archiveLogs is enabled
// (see workflow/executor/executor.go:saveContainerLogs).
// The key ends with ".log", which is not in the MIME database
// on minimal container images (e.g. distroless).
Name: "main-logs",
ArtifactLocation: wfv1.ArtifactLocation{
S3: &wfv1.S3Artifact{
Key: "my-wf/my-node-1/main.log",
},
},
},
},
},
},
Expand Down Expand Up @@ -474,6 +487,16 @@ func TestArtifactServer_GetArtifactFile(t *testing.T) {
statusCode: 200,
isDirectory: false,
},
{
path: "/artifact-files/my-ns/workflows/my-wf/my-node-1/outputs/main-logs",
statusCode: 200,
isDirectory: false,
// Verify that .log artifacts are served with a non-empty Content-Type.
// The ".log" extension may not be in the system MIME database on minimal
// container images (e.g. distroless), causing mime.TypeByExtension to
// return "". The fallback in returnArtifact ensures "text/plain; charset=utf-8"
// is used instead.
},
}

for _, tt := range tests {
Expand Down Expand Up @@ -502,6 +525,7 @@ func TestArtifactServer_GetArtifactFile(t *testing.T) {
}
} else {
assert.Equal(t, "my-data", string(all))
assert.NotEmpty(t, recorder.Header().Get("Content-Type"), "Content-Type header must not be empty")
}
}
})
Expand Down
Loading