From 03255bf3704a138f1938393fe592361adb3b6888 Mon Sep 17 00:00:00 2001 From: mohammadmseet-hue Date: Sat, 11 Apr 2026 18:51:25 +0200 Subject: [PATCH] Validate file paths in collector config and sanitize HTML in process table - manager/container.go: Verify that the resolved file path in ReadFile stays within the expected container root prefix after path.Join normalization. - containers.js: Add escapeHtml() to sanitize string values rendered in the process listing table where allowHtml is enabled, and use encodeURI for the cgroup link href. --- cmd/internal/pages/assets/js/containers.js | 21 ++++++++++++++------- manager/container.go | 4 ++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/cmd/internal/pages/assets/js/containers.js b/cmd/internal/pages/assets/js/containers.js index ee2f61867f..61e45d9abf 100644 --- a/cmd/internal/pages/assets/js/containers.js +++ b/cmd/internal/pages/assets/js/containers.js @@ -14,6 +14,12 @@ google.charts.load('current', {packages: ['corechart', 'gauge', 'default', 'format', 'ui', 'table']}); +// Escape HTML special characters to prevent XSS when rendering in tables with allowHtml. +function escapeHtml(str) { + if (typeof str !== 'string') return str; + return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); +} + function humanize(num, size, units) { var unit; for (unit = units.pop(); units.length && num >= size; unit = units.pop()) { @@ -622,10 +628,10 @@ function drawProcesses(isRoot, rootDir, processInfo) { var data = []; for (var i = 0; i < processInfo.length; i++) { var elements = []; - elements.push(processInfo[i].user); + elements.push(escapeHtml(processInfo[i].user)); elements.push(processInfo[i].pid); elements.push(processInfo[i].parent_pid); - elements.push(processInfo[i].start_time); + elements.push(escapeHtml(processInfo[i].start_time)); elements.push({ v: processInfo[i].percent_cpu, f: processInfo[i].percent_cpu.toFixed(2) @@ -639,15 +645,16 @@ function drawProcesses(isRoot, rootDir, processInfo) { v: processInfo[i].virtual_size, f: humanizeIEC(processInfo[i].virtual_size) }); - elements.push(processInfo[i].status); - elements.push(processInfo[i].running_time); - elements.push(processInfo[i].cmd); + elements.push(escapeHtml(processInfo[i].status)); + elements.push(escapeHtml(processInfo[i].running_time)); + elements.push(escapeHtml(processInfo[i].cmd)); elements.push(processInfo[i].psr); if (isRoot) { var cgroup = processInfo[i].cgroup_path; // Use the raw cgroup link as it works for all containers. - var cgroupLink = '' + - cgroup.substr(0, 30) + ' '; + var escapedCgroup = escapeHtml(cgroup); + var cgroupLink = '' + + escapedCgroup.substr(0, 30) + ' '; elements.push({v: cgroup, f: cgroupLink}); } data.push(elements); diff --git a/manager/container.go b/manager/container.go index 5020e945b2..f1ff86a746 100644 --- a/manager/container.go +++ b/manager/container.go @@ -261,6 +261,10 @@ func (cd *containerData) ReadFile(filepath string, inHostNamespace bool) ([]byte } for _, pid := range pids { filePath := path.Join(rootfs, "/proc", pid, "/root", filepath) + expectedPrefix := path.Join(rootfs, "/proc", pid, "/root") + "/" + if !strings.HasPrefix(filePath, expectedPrefix) { + return nil, fmt.Errorf("invalid file path %q: resolves outside container root", filepath) + } klog.V(3).Infof("Trying path %q", filePath) data, err := os.ReadFile(filePath) if err == nil {