aboutsummaryrefslogtreecommitdiff
path: root/internal/admin/api_players.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/admin/api_players.go')
-rw-r--r--internal/admin/api_players.go41
1 files changed, 40 insertions, 1 deletions
diff --git a/internal/admin/api_players.go b/internal/admin/api_players.go
index f7a0b87..f0bbe31 100644
--- a/internal/admin/api_players.go
+++ b/internal/admin/api_players.go
@@ -1,6 +1,7 @@
package admin
import (
+ "bytes"
"net/http"
"os"
"path/filepath"
@@ -157,6 +158,44 @@ func (s *AdminServer) getAccount(w http.ResponseWriter, r *http.Request, name st
delete(m, "password_hash")
m["_path"] = accPath
- m["_raw"] = string(data)
+ // _raw must not leak the password hash back over the API. Re-encode the
+ // parsed document with the password_hash value replaced so multiline and
+ // quoted values are redacted too — substring masking only covers the key's
+ // own line.
+ m["_raw"] = redactYAMLValue(data, "password_hash")
writeJSON(w, m)
}
+
+// redactYAMLValue returns data re-encoded with every top-level value for the
+// named key replaced by a fixed placeholder. Empty output falls back to the
+// original data so a parse failure never silently hides the document.
+func redactYAMLValue(data []byte, key string) string {
+ var node yaml.Node
+ if err := yaml.Unmarshal(data, &node); err != nil || len(node.Content) == 0 {
+ return string(data)
+ }
+ root := node.Content[0]
+ if root.Kind != yaml.MappingNode {
+ return string(data)
+ }
+ changed := false
+ for i := 0; i+1 < len(root.Content); i += 2 {
+ if root.Content[i].Value == key {
+ root.Content[i+1].Value = "[REDACTED]"
+ root.Content[i+1].Tag = "!!str"
+ root.Content[i+1].Style = 0
+ changed = true
+ }
+ }
+ if !changed {
+ return string(data)
+ }
+ var buf bytes.Buffer
+ enc := yaml.NewEncoder(&buf)
+ enc.SetIndent(2)
+ if err := enc.Encode(&node); err != nil {
+ return "[REDACTED]"
+ }
+ enc.Close()
+ return buf.String()
+}