Redact release deployment failure diagnostics
This commit is contained in:
+33
-8
@@ -1150,8 +1150,10 @@ function normalizeReleaseStatusService(service) {
|
||||
status: service?.status || service?.state || "ready",
|
||||
state: service?.state || service?.status || "ready",
|
||||
severity: service?.severity || "ok",
|
||||
message: service?.message || trFallback("status.service_ready", "Release service is ready."),
|
||||
next_action: service?.next_action || "",
|
||||
message: redactSensitiveDiagnostics(
|
||||
service?.message || trFallback("status.service_ready", "Release service is ready.")
|
||||
),
|
||||
next_action: redactSensitiveDiagnostics(service?.next_action || ""),
|
||||
target_tab: service?.target_tab || "overview",
|
||||
target_id: service?.target_id ?? null,
|
||||
deployment_id: service?.deployment_id ?? null,
|
||||
@@ -1175,8 +1177,8 @@ function normalizeReleaseStatusIssue(issue) {
|
||||
channel_slug: issue?.channel_slug || "",
|
||||
service_key: issue?.service_key || null,
|
||||
label: issue?.label || releaseStatusServiceLabel(issue?.service_key),
|
||||
message: issue?.message || "",
|
||||
next_action: issue?.next_action || "",
|
||||
message: redactSensitiveDiagnostics(issue?.message || ""),
|
||||
next_action: redactSensitiveDiagnostics(issue?.next_action || ""),
|
||||
target_tab: issue?.target_tab || "overview",
|
||||
target_id: issue?.target_id ?? null,
|
||||
deployment_id: issue?.deployment_id ?? null,
|
||||
@@ -4076,12 +4078,35 @@ function deploymentFailureSummary(deployment) {
|
||||
return summary && typeof summary === "object" ? summary : null;
|
||||
}
|
||||
|
||||
function redactSensitiveDiagnostics(value) {
|
||||
let text = String(value || "").trim();
|
||||
if (!text) {
|
||||
return "";
|
||||
}
|
||||
|
||||
text = text.replace(/(https?:\/\/)([^\s/@:]+):([^\s/@]+)@/gi, "$1[redacted]@");
|
||||
text = text.replace(/\b(?:gh[opsru]_|github_pat_|glpat-|xox[baprs]-)[A-Za-z0-9_\-]{8,}\b/g, "[redacted-token]");
|
||||
text = text.replace(/\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]{8,}/gi, "$1 [redacted]");
|
||||
text = text.replace(
|
||||
/\b((?:[A-Z][A-Z0-9_]*_)?(?:PASSWORD|PASSWD|SECRET|TOKEN|API[_-]?KEY|ACCESS[_-]?KEY|PRIVATE[_-]?KEY|AUTH)[A-Z0-9_]*)\s*=\s*(?:"[^"]*"|'[^']*'|[^\s,;]+)/gi,
|
||||
"$1=[redacted]"
|
||||
);
|
||||
text = text.replace(
|
||||
/\b(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3})\b/g,
|
||||
"[redacted-private-ip]"
|
||||
);
|
||||
text = text.replace(/\b[a-z0-9.-]+\.internal\b/gi, "[redacted-internal-host]");
|
||||
text = text.replace(/(?:^|\s)(?:\/[A-Za-z0-9._-]+){2,}\/\.env\b/g, " [redacted-env-path]");
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
function deploymentFailureCause(deployment) {
|
||||
return String(deploymentFailureSummary(deployment)?.root_cause || deployment?.error_message || "").trim();
|
||||
return redactSensitiveDiagnostics(deploymentFailureSummary(deployment)?.root_cause || deployment?.error_message || "");
|
||||
}
|
||||
|
||||
function deploymentFailureNextAction(deployment) {
|
||||
return String(deploymentFailureSummary(deployment)?.next_action || "").trim();
|
||||
return redactSensitiveDiagnostics(deploymentFailureSummary(deployment)?.next_action || "");
|
||||
}
|
||||
|
||||
function canPromoteDeployment(deployment) {
|
||||
@@ -4101,9 +4126,9 @@ function promotionBlockedReason(deployment) {
|
||||
return tr("actions.promote");
|
||||
}
|
||||
|
||||
return (
|
||||
return redactSensitiveDiagnostics(
|
||||
deployment?.promotion_blocked_reason ||
|
||||
trFallback("deployments.promotion_blocked", "Only successfully deployed release deployments can be promoted.")
|
||||
trFallback("deployments.promotion_blocked", "Only successfully deployed release deployments can be promoted.")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2231,6 +2231,35 @@ test("view-only release managers cannot search assignment subjects", async ({ pa
|
||||
expect(state.assignmentSubjectRequests || []).toEqual([]);
|
||||
});
|
||||
|
||||
test("view-only release managers see redacted deployment failure diagnostics", async ({ page }) => {
|
||||
const state = createReleaseState();
|
||||
state.deployments[0].error_message =
|
||||
"Clone failed for https://deploy:ghp_SECRET_TOKEN_123456789@github.internal/acme/api.git DB_PASSWORD=s3cr3t internal-host=10.0.4.12";
|
||||
state.deployments[0].failure_summary.root_cause = state.deployments[0].error_message;
|
||||
state.deployments[0].failure_summary.next_action =
|
||||
"Rotate ghp_SECRET_TOKEN_123456789 and inspect /var/lib/coolify/apps/api/.env before retrying.";
|
||||
|
||||
await boot(page, state, {
|
||||
permissions: ["superuser_release_manager_view"],
|
||||
});
|
||||
await page.goto("/superuser/configuration/releases/overview?channel=canary&app=api&branch=master", {
|
||||
waitUntil: "domcontentloaded",
|
||||
});
|
||||
|
||||
await expectReleaseManagerReady(page);
|
||||
await expect(page.getByTestId("release-manager-page")).not.toContainText("ghp_SECRET_TOKEN_123456789");
|
||||
await expect(page.getByTestId("release-manager-page")).not.toContainText("DB_PASSWORD=s3cr3t");
|
||||
await expect(page.getByTestId("release-manager-page")).not.toContainText("10.0.4.12");
|
||||
await expect(page.getByTestId("release-manager-page")).not.toContainText("github.internal");
|
||||
|
||||
await selectReleaseTab(page, "Deployments");
|
||||
await expect(page.getByTestId("release-deployment-failure-2")).toContainText("[redacted]");
|
||||
await expect(page.getByTestId("release-deployment-failure-2")).toContainText("DB_PASSWORD=[redacted]");
|
||||
await expect(page.getByTestId("release-deployment-failure-2")).toContainText("[redacted-private-ip]");
|
||||
await expect(page.getByTestId("release-deployment-failure-2")).toContainText("[redacted-internal-host]");
|
||||
await expect(page.getByTestId("release-deployment-failure-2")).toContainText("[redacted-env-path]");
|
||||
});
|
||||
|
||||
test("superusers manage release settings, assignments, integrations, and sync operations", async ({ page }) => {
|
||||
const state = await boot(page);
|
||||
await page.goto("/superuser/configuration/releases/overview?channel=canary&app=api&branch=canary", {
|
||||
|
||||
Reference in New Issue
Block a user