Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
6172ecd
fix(diagnostics): redact URL credentials from the debug support bundle
udsy19 Aug 23, 2026
4a2a6bf
fix(security): centralize full URL redaction
apurvvkumaria Aug 24, 2026
c2f2513
Merge branch 'main' into fix/debug-bundle-url-credential-redaction
apurvvkumaria Aug 24, 2026
6df9ef8
Merge branch 'main' into fix/debug-bundle-url-credential-redaction
apurvvkumaria Aug 24, 2026
224320b
Merge branch 'main' into fix/debug-bundle-url-credential-redaction
cv Aug 24, 2026
1cf47c1
Merge branch 'main' into fix/debug-bundle-url-credential-redaction
cv Aug 24, 2026
94c4fa4
Merge branch 'main' into fix/debug-bundle-url-credential-redaction
cv Aug 24, 2026
6891ac5
Merge branch 'main' into fix/debug-bundle-url-credential-redaction
cv Aug 24, 2026
4249965
Merge branch 'main' into fix/debug-bundle-url-credential-redaction
cv Aug 24, 2026
6aa02e4
Merge branch 'main' into fix/debug-bundle-url-credential-redaction
cv Aug 24, 2026
805b49a
Merge branch 'main' into fix/debug-bundle-url-credential-redaction
cv Aug 24, 2026
73f0ca9
test(diagnostics): keep URL cases with their owner
apurvvkumaria Aug 24, 2026
7161f8c
Merge branch 'main' into fix/debug-bundle-url-credential-redaction
apurvvkumaria Aug 24, 2026
dda4f0e
fix(ci): recalibrate integration shard ownership
apurvvkumaria Aug 24, 2026
8ba445e
merge: update branch from main
prekshivyas Aug 24, 2026
ca34729
Merge branch 'main' into fix/debug-bundle-url-credential-redaction
prekshivyas Aug 24, 2026
531e27c
Merge branch 'main' into fix/debug-bundle-url-credential-redaction
prekshivyas Aug 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions src/lib/actions/sandbox/gateway-restart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
import { GATEWAY_RESTART_MARKERS as MARKERS } from "../../agent/gateway-restart-markers";
import * as agentRuntime from "../../agent/runtime";
import { G, R } from "../../cli/terminal-style";
import { redactFull, redactUrl } from "../../security/redact";
import { URL_TOKEN_PATTERN } from "../../security/redact-url";
import { redactFullWithUrls } from "../../security/redact";
import { hermesMcpReconciliationRemediationLines } from "./mcp-bridge-hermes-reconciliation";
import { inspectHermesMcpReconciliationRefusal } from "./mcp-bridge-recovery";
import { assertHermesPortableCommandUnavailable } from "../../onboard/experimental/portable-agent-lifecycle";
Expand Down Expand Up @@ -173,11 +172,7 @@ const ANSI_CONTROL_RE =

function sanitizeGatewayRestartFailureLine(line: string): string {
const withoutControls = line.replace(ANSI_CONTROL_RE, "");
const withRedactedUrls = withoutControls.replace(
URL_TOKEN_PATTERN,
(url) => redactUrl(url) ?? "<REDACTED>",
);
return redactFull(withRedactedUrls);
return redactFullWithUrls(withoutControls);
}

function sanitizeGatewayRestartFailureDetail(detail: string): string {
Expand Down
11 changes: 11 additions & 0 deletions src/lib/diagnostics/debug.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ describe("redact", () => {
const clean = "Hello world, no secrets here";
expect(redact(clean)).toBe(clean);
});

it("removes credentials from a URL", () => {
expect(redact("proxy: https://service-user:service-password@example.com/path")).toBe(
"proxy: https://example.com/path",
);
});

it("leaves a credential-free URL readable", () => {
const url = "https://integrate.api.nvidia.com/v1/models";
expect(redact(`probing ${url}`)).toBe(`probing ${url}`);
});
});

describe("createTarball", () => {
Expand Down
10 changes: 8 additions & 2 deletions src/lib/diagnostics/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,15 @@ function section(title: string): void {
// Secret redaction — delegates to unified redact module (#2381).
// ---------------------------------------------------------------------------

import { redactFull as redact } from "../security/redact";
import { redactFullWithUrls } from "../security/redact";

export { redact };
/**
* Redact collected diagnostics before they are written to the bundle or
* echoed to the terminal.
*/
export function redact(text: string): string {
return redactFullWithUrls(text);
}

// ---------------------------------------------------------------------------
// Command runner
Expand Down
6 changes: 2 additions & 4 deletions src/lib/policy/preset-scope-render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import { isObjectRecord } from "../core/json-types";
import { redactFull, redactUrl } from "../security/redact";
import { URL_TOKEN_PATTERN } from "../security/redact-url";
import { redactFullWithUrls } from "../security/redact";
import { type PolicyValue, parseNetworkPolicies } from "./preset-parsing";

type RuleScope = {
Expand Down Expand Up @@ -58,8 +57,7 @@ export function escapeTerminalText(value: string): string {

/** Redact credential-shaped content before rendering untrusted YAML scalars. */
function renderTerminalText(value: string): string {
const redactedUrls = value.replace(URL_TOKEN_PATTERN, (url) => redactUrl(url) ?? "<REDACTED>");
return escapeTerminalText(redactFull(redactedUrls));
return escapeTerminalText(redactFullWithUrls(value));
}

function toStringOrUndefined(value: PolicyValue | undefined): string | undefined {
Expand Down
26 changes: 25 additions & 1 deletion src/lib/security/redact.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,31 @@

import { describe, expect, it } from "vitest";

import { redactForLog, redactFull, redactLogSequence, redactSensitiveText } from "./redact.js";
import {
redactForLog,
redactFull,
redactFullWithUrls,
redactLogSequence,
redactSensitiveText,
} from "./redact.js";

describe("redactFullWithUrls", () => {
it.each([
[
"username and password",
"https://service-user:service-password@example.com/path",
"https://example.com/path",
],
["userinfo only", "https://service-token@example.com/path", "https://example.com/path"],
[
"malformed URL fallback",
"https://fallback-user:fallback-password@[not-an-ip/path",
"https://[not-an-ip/path",
],
])("fully redacts URL credentials for %s", (_case, value, expected) => {
expect(redactFullWithUrls(value)).toBe(expected);
});
});

describe("redactForLog", () => {
it("redacts pass aliases in structured keys", () => {
Expand Down
9 changes: 8 additions & 1 deletion src/lib/security/redact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import type { StdioOptions } from "node:child_process";
*
* Two modes:
* - `redact()` — partial (keep first 4 chars). Used by runner.ts for CLI output.
* - `redactFull()` — full replacement. Used by debug.ts for diagnostic dumps.
* - `redactFull()` — full replacement for known secret patterns.
* - `redactFullWithUrls()` — full replacement for known patterns and URL credentials.
* - `redactSensitiveText()` — full replacement + truncation. Used by onboard-session.ts.
*
* Ref: https://github.com/NVIDIA/NemoClaw/issues/2381
Expand Down Expand Up @@ -194,6 +195,12 @@ export function redactFull(text: string): string {
return result;
}

/** Fully redact secret patterns and credentials embedded in URL tokens. */
export function redactFullWithUrls(text: string): string {
const redactedUrls = text.replace(URL_TOKEN_PATTERN, (url) => redactUrl(url) ?? "<REDACTED>");
return redactFull(redactedUrls);
}

function redactStandaloneSecrets(text: string, replacement: string): string {
let result = text;
for (const pattern of [
Expand Down
Loading