|
if isDir { |
|
// return an html page to the user |
|
|
|
objects, err := driver.ListObjects(ctx, artifact) |
|
if err != nil { |
|
a.httpFromError(ctx, err, w) |
|
return |
|
} |
|
a.logger.WithFields(logging.Fields{ |
|
"artifact": artifact, |
|
"files": objects, |
|
}).Debug(ctx, "this is a directory") |
|
|
|
key, _ := artifact.GetKey() |
|
for _, object := range objects { |
|
|
|
// object is prefixed by the key, we must trim it |
|
dir, file := path.Split(strings.TrimPrefix(object, key+"/")) |
|
|
|
// if dir is empty string, we are in the root dir |
|
// we found in index.html, abort and redirect there |
|
if dir == "" && file == "index.html" { |
|
w.Header().Set("Location", r.URL.String()+"index.html") |
|
w.WriteHeader(http.StatusTemporaryRedirect) |
|
return |
|
} |
|
} |
|
|
|
w.WriteHeader(http.StatusOK) |
|
_, _ = w.Write([]byte("<html><body><ul>\n")) |
|
|
|
dirs := map[string]bool{} // to de-dupe sub-dirs |
|
|
|
_, _ = fmt.Fprintf(w, "<li><a href=\"%s\">%s</a></li>\n", "..", "..") |
|
|
|
for _, object := range objects { |
|
|
|
// object is prefixed the key, we must trim it |
|
dir, file := path.Split(strings.TrimPrefix(object, key+"/")) |
|
|
|
// if dir is empty string, we are in the root dir |
|
if dir == "" { |
|
_, _ = fmt.Fprintf(w, "<li><a href=\"%s\">%s</a></li>\n", file, file) |
|
} else if dirs[dir] { |
|
continue |
|
} else { |
|
_, _ = fmt.Fprintf(w, "<li><a href=\"%s\">%s</a></li>\n", dir, dir) |
|
dirs[dir] = true |
|
} |
|
} |
|
_, _ = w.Write([]byte("</ul></body></html>")) |
Summary
Stored XSS in the artifact directory listing allows any workflow author to execute arbitrary JavaScript in another user’s browser under the Argo Server origin, enabling API actions with the victim’s privileges.
Details
The directory listing response in
server/artifacts/artifact_server.gorenders object names directly into HTML viafmt.Fprintfwithout escaping. Object names come fromdriver.ListObjects(...)and are attacker‑controlled when a workflow writes files into an output artifact directory.argo-workflows/server/artifacts/artifact_server.go
Lines 194 to 244 in 9872c29
PoC
https://localhost:2746/artifact-files/argo/workflows/<wf-name>/<node-id>/outputs/dir/Impact
As the script has access to the Argo Server API (as the victim), so may do the following (if the victim may):