Skip to content

fix(images): report MCP discovery assertion failures - #8742

Merged
jyaunches merged 2 commits into
mainfrom
codex/managed-image-discovery-diagnostics
Aug 10, 2026
Merged

fix(images): report MCP discovery assertion failures#8742
jyaunches merged 2 commits into
mainfrom
codex/managed-image-discovery-diagnostics

Conversation

@jyaunches

@jyaunches jyaunches commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Brev image run 31352614414 failed the shared managed-image RUN without emitting a concrete assertion name. This change adds named, fail-closed diagnostics for MCP discovery bundle execution, JSON-contract validation, discovery-tree traversal, and the first unsafe path while preserving every existing predicate, assertion, and successful behavior. Contract mismatches report bounded, credential-redacted actual fields beside the expected contract.

Related Issue

Related to #8665. PR #8702 addresses the image-metadata root cause and overlaps the same shared block; this PR does not close the issue.

Changes

  • Report the bundle or find exit status before another command can overwrite it.
  • Keep the existing three-field discovery contract and acceptance of extra fields, but report its sanitized actual response and exact expected response on failure.
  • Mirror NemoClaw's current credential-pattern families in the bounded response diagnostic, including malformed JSON and private-key material.
  • Report the first unsafe path with dereferenced UID, GID, file type, and mode plus the path's symlink status. Dangling links retain link metadata.
  • Apply the same block to the OpenClaw, Hermes, and Deep Code Dockerfiles without changing the five managed-runtime assertions added by fix(images): report managed runtime assertion failures #8673.
  • Execute every new failure message and the unchanged success path through the shared contract helper for all three Dockerfiles.
  • Preserve the evidence boundary: the run log proves that no guarded assertion failed, while the previously silent discovery prefix did not identify which check stopped the build.
  • Do not dispatch another Brev image build; downstream image validation remains after merge.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification: Internal image-build failure diagnostics and contract coverage only; no public command, configuration, default, successful runtime behavior, or documented workflow changes.
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: Codex Desktop independently reviewed head f67df5af4 against the security rubric. The review found and verified fixes for canonical credential redaction and find -L referent metadata, then reported PASS with no remaining publication blocker.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Documentation Writer Review

  • Documentation writer subagent reviewed the completed changes
  • Result: no-docs-needed
  • Evidence: No documentation paths changed. The final diff contains internal managed-image build diagnostics and their contract tests only.
  • Agent: Codex Desktop

DGX Station Hardware Evidence

  • Tested on DGX Station
  • Tested commit:
  • Station profile/scenario:
  • Result:
  • Supporting evidence:

Verification

  • PR description includes a Signed-off-by: line and every commit appears as Verified in GitHub
  • Normal pre-commit, commit-msg, and pre-push hooks passed, or npm run validate:pr passed after refreshing origin/main when hooks were skipped or unavailable
  • Targeted behavior tests pass for the current change set, or tests are marked not applicable above — npm exec -- vitest run --project integration test/mcp-tool-discovery-image-contract.test.ts test/openclaw-final-image-layout.test.ts test/hermes-final-image-layout.test.ts test/langchain-deepagents-code-image.test.ts — 4 files and 49 tests passed.
  • Applicable broad gate passed — npm test for broad runtime/test-harness changes; npm run check for repo-wide validation/coverage changes — not applicable because the change is limited to one managed-image contract helper and its three focused consumers.
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Julie Yaunches jyaunches@nvidia.com

Summary by CodeRabbit

  • Bug Fixes
    • Improved managed image validation for symlink targets and filesystem safety.
    • MCP tool discovery now distinguishes execution and scanning failures and reports explicit command statuses.
    • Invalid or unexpected discovery data is handled safely with clearer contract validation.
    • Diagnostic output now redacts credential- and private-key-like values.
  • Tests
    • Expanded coverage for discovery failures, malformed data, unsafe permissions, symlinks, and successful validation.

@jyaunches jyaunches self-assigned this Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change hardens managed-image validation across three Dockerfiles and its contract test. It adds dereferenced symlink checks, explicit command-status diagnostics, defensive discovery JSON validation, secret redaction, and coverage for discovery and runtime-tree failures.

Changes

Managed image validation

Layer / File(s) Summary
Dereferenced metadata checks
Dockerfile, agents/hermes/Dockerfile, agents/langchain-deepagents-code/Dockerfile
Runtime assertions can inspect symlink targets with stat -L while retaining non-dereferenced checks by default.
Discovery command and contract validation
Dockerfile, agents/hermes/Dockerfile, agents/langchain-deepagents-code/Dockerfile
Discovery execution and tree scans report exit statuses. JSON validation handles malformed or unexpected output and redacts sensitive or oversized diagnostics.
Discovery harness and validation cases
test/support/managed-bootstrap-image-contract.ts
The test harness mocks discovery and filesystem commands. Tests cover command failures, secret redaction, invalid JSON, unsafe metadata, symlinks, and successful discovery.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ManagedImageAssertions
  participant MCPDiscovery
  participant JSONContractValidator
  participant RuntimeTreeScanner
  ManagedImageAssertions->>MCPDiscovery: Execute discovery bundle
  MCPDiscovery-->>JSONContractValidator: Return JSON output
  JSONContractValidator-->>ManagedImageAssertions: Validate contract and sanitize diagnostics
  ManagedImageAssertions->>RuntimeTreeScanner: Scan runtime tree
  RuntimeTreeScanner-->>ManagedImageAssertions: Return command status and dereferenced metadata
Loading

Possibly related PRs

Suggested labels: area: security, area: sandbox

Suggested reviewers: cv, senthilr-nv

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: improved reporting for MCP discovery assertion failures in managed images.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/managed-image-discovery-diagnostics

Comment @coderabbitai help to get the list of available commands.

@github-code-quality

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in commit f67df5a in the codex/managed-image-... branch remains at 96%, unchanged from commit acf810d in the main branch.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Dockerfile`:
- Line 1518: Update the named-secret redaction matchers in Dockerfile:1518-1518,
agents/hermes/Dockerfile:380-380, and
agents/langchain-deepagents-code/Dockerfile:130-130 to redact any non-empty
value, removing the ten-character minimum while preserving the existing
field-name matching. In
test/support/managed-bootstrap-image-contract.ts:223-239, add negative-path
regression cases for short TOKEN and PASSWORD values and verify the resulting
JSON-contract errors do not expose those secrets.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: c9805239-6371-47fd-a85f-a946e1426b9d

📥 Commits

Reviewing files that changed from the base of the PR and between acf810d and f67df5a.

📒 Files selected for processing (4)
  • Dockerfile
  • agents/hermes/Dockerfile
  • agents/langchain-deepagents-code/Dockerfile
  • test/support/managed-bootstrap-image-contract.ts

Comment thread Dockerfile
&& discovery_unsafe="$(find -L /usr/local/lib/nemoclaw/mcp-tool-discovery-runtime \( ! -user root -o -perm /022 \) -print -quit)" \
&& test -z "$discovery_unsafe" \
|| managed_image_command_failed mcp-tool-discovery-bundle-execution "$?"; \
node -e 'const expected = { protocol: 1, ok: false, detail: "tool discovery received invalid runtime arguments" }; const standaloneSecretPatterns = [/(?:nvapi-|nvcf-|gh[pousr]_|sk-proj-|sk-ant-|hf_|glpat-|gsk_|pypi-|tvly-)[A-Za-z0-9_-]{10,}/gu, /github_pat_[A-Za-z0-9_]{30,}/gu, /sk-[A-Za-z0-9_-]{20,}/gu, /(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/gu, /A(?:K|S)IA[A-Z0-9]{16}/gu, /\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/gu, /\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/gu, /lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/gu, /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/gu, /\b[A-Za-z0-9_=-]{32,}\b/gu]; const redactContextSecrets = (value) => value.replace(/\b(?:Bearer|Basic)\s+\S+/giu, "<REDACTED>").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}_(?:KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)|(?:X[-_])?API[-_]KEY|TOKEN|SECRET|CREDENTIAL|PASSWORD|PASSWD|PASS)["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]{10,}/giu, (_match, prefix) => prefix + "<REDACTED>").replace(/((?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]{1,128}(?:Token|Secret|Credential)|[A-Za-z0-9]{0,128}(?:[Aa]ccess|[Rr]efresh|[Cc]lient|[Bb]earer|[Aa]uth|[Aa][Pp][Ii]|[Pp]rivate|[Ss]igning|[Ss]ession|[Bb]ot|[Aa]pp|[Rr]esolved)Key|[A-Za-z0-9]{1,128}(?:Password|Passwd|Pass))["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]{10,}/gu, (_match, prefix) => prefix + "<REDACTED>").replace(/((?:^|[^A-Za-z0-9])KEY["\x27]?(?:[ \t]{0,32}[=:][ \t]{0,32}|[ \t]{1,32})["\x27]?)[^\s"\x27]{10,}/gu, (_match, prefix) => prefix + "<REDACTED>"); const sanitize = (value) => { if (value === undefined) return "<missing>"; if (value === null || typeof value === "boolean" || typeof value === "number") return value; if (typeof value !== "string") return "<" + (Array.isArray(value) ? "array" : typeof value) + ">"; let printable = value.replace(/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----[\s\S]*/gu, "<REDACTED>").replace(/[^\x20-\x7e]/gu, "?"); for (const pattern of standaloneSecretPatterns) printable = printable.replace(pattern, "<REDACTED>"); printable = redactContextSecrets(printable); return printable.length <= 240 ? printable : printable.slice(0, 237) + "..."; }; let result; let parsed = true; try { result = JSON.parse(process.argv[1]); } catch { parsed = false; } const record = parsed && result !== null && typeof result === "object" && !Array.isArray(result) ? result : undefined; if (record && record.protocol === expected.protocol && record.ok === expected.ok && record.detail === expected.detail) process.exit(0); const actual = record ? { protocol: sanitize(record.protocol), ok: sanitize(record.ok), detail: sanitize(record.detail) } : parsed ? { type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result, value: sanitize(result) } : { type: "invalid-json", preview: sanitize(process.argv[1]) }; console.error("ERROR: managed image assertion failed: mcp-tool-discovery-json-contract actual=%s expected=%s", JSON.stringify(actual), JSON.stringify(expected)); process.exit(1);' "$discovery_contract" \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Redact short values for named secret fields.

TOKEN=short and PASSWORD=secret do not match any current redaction pattern. The JSON-contract failure then writes those values to the Docker build log. Remove the {10,} minimum from each named-secret value matcher, while still requiring a non-empty value. Add regression cases for short TOKEN and PASSWORD values.

  • Dockerfile#L1518-L1518: redact non-empty short named secret values.
  • agents/hermes/Dockerfile#L380-L380: apply the same redaction change.
  • agents/langchain-deepagents-code/Dockerfile#L130-L130: apply the same redaction change.
  • test/support/managed-bootstrap-image-contract.ts#L223-L239: add short named-secret regression cases.

As per path instructions, require negative-path tests that prove the boundary does not leak secrets in errors.

📍 Affects 4 files
  • Dockerfile#L1518-L1518 (this comment)
  • agents/hermes/Dockerfile#L380-L380
  • agents/langchain-deepagents-code/Dockerfile#L130-L130
  • test/support/managed-bootstrap-image-contract.ts#L223-L239
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` at line 1518, Update the named-secret redaction matchers in
Dockerfile:1518-1518, agents/hermes/Dockerfile:380-380, and
agents/langchain-deepagents-code/Dockerfile:130-130 to redact any non-empty
value, removing the ten-character minimum while preserving the existing
field-name matching. In
test/support/managed-bootstrap-image-contract.ts:223-239, add negative-path
regression cases for short TOKEN and PASSWORD values and verify the resulting
JSON-contract errors do not expose those secrets.

Source: Path instructions

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings reported

Advisor assessment: No blocking advisor findings reported
Next action: No advisor follow-up needed.
Findings: 0 blockers · 0 warnings · 0 suggestions

Model lanes

  • GPT-5.6 Terra (primary): Completed · high confidence · 0 blockers · 0 warnings · 0 suggestions
  • Nemotron 3 Ultra (second opinion): Failed after a partial review · low confidence · 0 blockers · 0 warnings · 0 suggestions

Second-opinion terminology and E2E selections are advisory. Live E2E does not run automatically for pull requests.

E2E guidance

Advisory only. A maintainer can dispatch the default E2E suite against this exact revision.

Recommended E2E: cloud-onboard, full-e2e, hermes-e2e, hermes-inference-switch, managed-image-multiarch-startup, security-posture, ubuntu-repo-cloud-langchain-deepagents-code

Workflow run details

This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge.

@jyaunches
jyaunches merged commit d097a22 into main Aug 10, 2026
73 of 75 checks passed
@jyaunches
jyaunches deleted the codex/managed-image-discovery-diagnostics branch August 10, 2026 14:06
@wscurran wscurran added chore Build, CI, dependency, or tooling maintenance area: packaging Packages, images, registries, installers, or distribution area: observability Logging, metrics, tracing, diagnostics, or debug output labels Aug 10, 2026
prekshivyas added a commit that referenced this pull request Aug 11, 2026
## Summary

The discovery-contract diagnostic added in #8742 redacts named secret
values only when they contain at least 10 characters, so shorter values
can reach CI stderr. This change redacts every matched nonempty named
value while preserving the existing JSON contract, fail-closed behavior,
and successful image build path.

## Related Issue

Follow-up to #8742. Addresses the unresolved [short named-secret review
finding](#8742 (comment)).

## Changes

- Redact one-character and longer named credential values in each of the
three managed-image Dockerfiles.
- Execute one-character token and password contract failures through the
shared image-contract helper and verify that stderr contains only the
redacted diagnostic for all three Dockerfiles.

## Type of Change

- [x] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates

- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [ ] Docs updated for user-facing behavior changes
- [x] Docs not applicable — justification: This is an internal
build-diagnostic redaction correction with no supported user-facing
behavior change.
- [x] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [x] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification: Independent Codex
security re-review of `d14fab714` passed with no publication blocker; it
confirmed identical fail-closed behavior across all three Dockerfiles
and executable redaction coverage through all three contract callers.
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Documentation Writer Review

- [x] Documentation writer subagent reviewed the completed changes
- Result: `no-docs-needed`
- Evidence: The change only tightens internal diagnostic redaction and
adds contract-test inputs; no public API, CLI, configuration, workflow,
or documented behavior changes.
- Agent: Codex Desktop
<!-- docs-review-head-sha: d14fab7 -->
<!-- docs-review-agents-blob-sha: c4923a3 -->

## DGX Station Hardware Evidence

- [ ] Tested on DGX Station
- Tested commit: Not applicable
- Station profile/scenario: Not applicable
- Result: Not applicable
- Supporting evidence: Not applicable

## Verification

- [x] PR description includes a `Signed-off-by:` line and every commit
appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run validate:pr` passed after refreshing `origin/main` when hooks
were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — command/result or justification: `npm
exec -- vitest run --project integration
test/mcp-tool-discovery-image-contract.test.ts
test/openclaw-final-image-layout.test.ts
test/hermes-final-image-layout.test.ts
test/langchain-deepagents-code-image.test.ts` passed (49 tests in 4
files).
- [x] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result: Not applicable to this
three-regex correction and two shared contract inputs; `npm run
validate:pr` passed.
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

---

Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Bug Fixes**
- Improved diagnostic redaction to hide context-based secrets regardless
of value length.
- Short credential values, including tokens and passwords, are now
protected from appearing in managed-image diagnostic output.
- Existing standalone secret detection and diagnostic validation remain
unchanged.

- **Tests**
- Added coverage confirming that token and password assignments are
properly redacted.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: Prekshi Vyas <34834085+prekshivyas@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: observability Logging, metrics, tracing, diagnostics, or debug output area: packaging Packages, images, registries, installers, or distribution chore Build, CI, dependency, or tooling maintenance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants