NVIDIA / NVIDIA/NemoClaw

security: verify runtime OpenShell version against credential boundary manifest

Open
#6,256 2 comments 0 reactions 1 assignee Claimed by @ericksoa View on GitHub
area: security needs: unblock
Dominant language
TypeScript
Stars
22.5k
Forks
3.1k
Avg merge
1d 1h
Merged PRs (30d)
715

Description

## Source

Identified in [PR #5876 — feat(mcp): add OpenShell-managed MCP servers](https://github.com/NVIDIA/NemoClaw/pull/5876), specifically the [PR Review Advisor findings](https://github.com/NVIDIA/NemoClaw/pull/5876#issuecomment-4812614670).

## Problem

The credential boundary manifest `openshell-child-visible-credentials.v0.0.72.json` is baked for OpenShell 0.0.72 and supplies the deny-lists used to protect child-visible credentials. The TypeScript and Python consumers validate the manifest structure, but neither verifies that the running `openshell` binary matches `manifest.openshellVersion`. The Hermes image and startup path also lack an equivalent runtime gate.

A sandbox can therefore use credential-boundary rules for OpenShell 0.0.72 while actually running a newer or older OpenShell after an image, installer, or manual runtime change.

## Findings consolidated

### PRA-4 — TypeScript validation boundary

Affected paths:

- `src/lib/actions/sandbox/mcp-bridge-validation.ts`
- `src/lib/security/mcp-url-target.ts`

The manifest is imported and its `runtimeControlKeys`, `runtimeControlPrefixes`, `rawChildValueKeys`, and `rewrittenChildValueKeys` are used without executing `openshell --version`.

### PRA-5 — Python Hermes transaction boundary

Affected path:

- `agents/hermes/mcp-config-transaction.py`

`_load_credential_boundary_manifest()` checks the baked manifest version but never compares it with the installed OpenShell binary.

### PRA-6 — Hermes runtime/startup boundary

Affected paths:

- `agents/hermes/Dockerfile`
- `agents/hermes/start.sh`
- `agents/hermes/mcp-config-transaction.py`

The Dockerfile checks MCP Python imports at build time, but Hermes startup does not verify the runtime OpenShell version before using the credential deny-list.

## Security impact

When the manifest and runtime diverge:

- newly reserved OpenShell credential names may be accepted and exposed across the child credential boundary;
- names that are legitimate for the running version may be rejected;
- the credential isolation contract silently depends on stale metadata.

## Required behavior

- Execute and parse `openshell --version` in the TypeScript credential-validation path.
- Execute and parse `subprocess.run(["openshell", "--version"], capture_output=True, text=True)` in the Python/Hermes path.
- Compare the parsed version exactly with `manifest.openshellVersion`.
- Fail closed when the binary is missing, exits unsuccessfully, produces unparseable output, or reports a different version.
- Ensure the Hermes check runs for every relevant startup. If the Python transaction is guaranteed to execute before credential configuration on every startup, it may satisfy the startup boundary; otherwise add an entrypoint check.
- A short-lived cache may be used in frequently invoked TypeScript validation, but it must not allow a stale result to survive a runtime change indefinitely.
- Error output should identify expected versus actual version without exposing credential values.

## Regression coverage

- Add TypeScript tests in `src/lib/actions/sandbox/mcp-bridge-input-validation.test.ts` for:
- matching runtime and manifest versions;
- runtime version 0.0.73 with a 0.0.72 manifest;
- missing binary;
- non-zero exit;
- unparseable version output.
- Add equivalent Python transaction coverage in `test/hermes-mcp-config-transaction.test.ts`.
- Add a Hermes runtime/E2E test in `test/e2e/live/rebuild-hermes.test.ts` that substitutes a mismatched OpenShell version and proves startup or credential configuration fails closed.

## Acceptance criteria

- TypeScript and Hermes/Python credential-boundary paths verify the actual runtime OpenShell version before applying manifest deny-lists.
- Exact matches proceed normally.
- Mismatch, missing binary, command failure, and parse failure are rejected before credentials are written or the affected runtime starts.
- Unit tests cover both implementations.
- Hermes runtime coverage proves the check cannot be bypassed by starting an image with a different OpenShell binary.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.