Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions agents/hermes/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# Layers PR-specific code (plugin, config, startup script) on top of the
# pre-built Hermes base image. Mirrors the OpenClaw Dockerfile structure.

ARG BASE_IMAGE=ghcr.io/nvidia/nemoclaw/hermes-sandbox-base@sha256:4295138eb70f938189430f8dc7b3cd5db0aa762234e64e398a6a5ef60803126c
ARG BASE_IMAGE=ghcr.io/nvidia/nemoclaw/hermes-sandbox-base@sha256:ffafa4dd1d8d5a802ae4fc4005b51e1accfa5e782e47de736a0d8d8bf2c83837
ARG NEMOCLAW_CORPORATE_CA_B64=
ARG NEMOCLAW_MANAGED_IMAGE_CAPABILITY_UNION=0

Expand Down Expand Up @@ -662,7 +662,7 @@ RUN node --experimental-strip-types \
ARG NEMOCLAW_HERMES_WRAPPER_SHA256=f4276e9833638b7a620176c88bd329d6b6d4948538a3227b727a1397146a0e0e
ARG NEMOCLAW_HERMES_CLI_ADAPTER_SHA256=989edf54a8c09c6efb348600a8aa2f264c0b71408eb9d7bcd579b92cbeccf9b1
ARG NEMOCLAW_HERMES_CLI_ADAPTER_VALIDATOR_SHA256=db4046e79e513eab67b069a8eda20167b8b65529cf26842531d2ad673c670330
ARG NEMOCLAW_HERMES_VALIDATOR_SHA256=a0c87387c0a00e7aad5892375303115577d72293ea6af7d15d1861e6a02c62c6
ARG NEMOCLAW_HERMES_VALIDATOR_SHA256=5cdac58c052f1df745d70f0126bc658eff1daef745688dc7570ca7795c068228
ARG NEMOCLAW_HERMES_TIRITH_FINALIZER_SHA256=a1e6b1c53ab297569abb87c29d15c294d729e46005bfd022136b4c447a791819
ARG NEMOCLAW_HERMES_CRON_RESTORE_CONTROLLER_SHA256=e8593cf1580bffa4663e91c079ba0ce31c3d26391f5b1718872701138ce250b0
# hadolint ignore=DL4006
Expand Down
1 change: 1 addition & 0 deletions agents/hermes/validate-env-secret-boundary.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"API_SERVER_HOST",
"API_SERVER_PORT",
"GPG_KEY",
"NEMOCLAW_HERMES_API_PORT",
"NEMOCLAW_INFERENCE_API",
"NEMOCLAW_INFERENCE_PROVIDER_ID",
"NEMOCLAW_PROVIDER_KEY",
Expand Down
74 changes: 49 additions & 25 deletions docs/security/hermes-0.19.0-dependency-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,15 @@ The Hermes sandbox image build fails if either installed dependency has another
The base image also replaces the published `python-multipart==0.0.27` lock resolution with the hash-verified and attested `python-multipart==0.0.32`.
The base image overlays checksum-pinned Node.js `24.18.1` archives for both supported architectures and installs exact uv `0.11.33`; build-time assertions reject version drift before Hermes is installed.

The `BASE_IMAGE` argument in `agents/hermes/Dockerfile` pins the patched multi-platform Open Container Initiative (OCI) index `sha256:4295138eb70f938189430f8dc7b3cd5db0aa762234e64e398a6a5ef60803126c`.
GitHub Actions workflow `.github/workflows/base-image.yaml` run `31636995117`, attempt 1, built and published that replacement image from source commit `7c721ae4d60fd54e11f4d0c7d0482ccd6ac8cded`, including the exact-source dashboard WhatsApp session-path patch and `libexpat1==2.8.3-1` for both supported architectures.
It supersedes index `sha256:3d54b928baef9df403227e846f73079d13ca8424a27cd5268ca97bac3f030b27`, which workflow run `31031662054`, attempt 1, published from source commit `a7a7f3e470a75c404d316d2054445e16bb63b48c`.
The selected image's `linux/amd64` and `linux/arm64` base-image builds passed the exact-source patch guard, locked bridge install, bridge-to-Baileys option assertions, and controlled-proxy WebSocket `CONNECT` regression.
GitHub Actions must build the Hermes sandbox image from the selected base image, and the required live end-to-end (E2E) checks must pass before approval.
The `BASE_IMAGE` argument in `agents/hermes/Dockerfile` pins the patched multi-platform Open Container Initiative (OCI) index `sha256:ffafa4dd1d8d5a802ae4fc4005b51e1accfa5e782e47de736a0d8d8bf2c83837`.
GitHub Actions workflow `.github/workflows/base-image.yaml` run `31717470863`, attempt 1, built and published that replacement image from source commit `d243ea62509bae7832a23fe8636e947303c19c60`.
It supersedes index `sha256:4295138eb70f938189430f8dc7b3cd5db0aa762234e64e398a6a5ef60803126c`, which workflow run `31636995117`, attempt 1, published from source commit `7c721ae4d60fd54e11f4d0c7d0482ccd6ac8cded`.
That superseded index had replaced index `sha256:3d54b928baef9df403227e846f73079d13ca8424a27cd5268ca97bac3f030b27`, which workflow run `31031662054`, attempt 1, published from source commit `a7a7f3e470a75c404d316d2054445e16bb63b48c`.
Both platform base-image jobs completed successfully.
BuildKit restored the package layers containing the exact `dpkg` assertions for `vim-common=2:9.2.0858-1`, `vim-tiny=2:9.2.0858-1`, and `libssh2-1t64=1.11.1-1+deb13u1+nemoclaw2` from cache.
The same run built and validated Hermes managed images from child manifests `sha256:da722766abb3c55d20242c3c62b434fd09583e4d92b5682aa98b374a74e05fa1` and `sha256:c7fe8f5664beaf5f10ba990f1317d17a976ece1093a72f9efb0effdf93f3f48d`.
Both `Validate exact managed image before promotion` steps completed successfully, confirming that the completed-image verifier passed with the selected base image.
The required live end-to-end (E2E) checks remain an approval gate.

## Reviewed identities

Expand Down Expand Up @@ -247,31 +251,50 @@ The `BASE_IMAGE` argument in `agents/hermes/Dockerfile` pins the following publi
| Workflow | `Images / Base Images` |
| Workflow path | `.github/workflows/base-image.yaml` |
| Trigger | `push` to `main` |
| Producer run | `31636995117`, attempt 1, completed successfully |
| Source commit | `7c721ae4d60fd54e11f4d0c7d0482ccd6ac8cded` |
| OCI index | `sha256:4295138eb70f938189430f8dc7b3cd5db0aa762234e64e398a6a5ef60803126c` |
| Producer run | `31717470863`, attempt 1, completed successfully |
| Source commit | `d243ea62509bae7832a23fe8636e947303c19c60` |
| OCI index | `sha256:ffafa4dd1d8d5a802ae4fc4005b51e1accfa5e782e47de736a0d8d8bf2c83837` |

The selected index resolves to these platform manifests and image-configuration labels:
The selected index resolves to these platform manifests:

| Platform | Child manifest | `org.opencontainers.image.source` | `org.opencontainers.image.revision` |
| --- | --- | --- | --- |
| `linux/amd64` | `sha256:f82972cf3d1497e60741ae0c48a870030d792e56652ee35868d30099cd93d831` | `https://github.com/NVIDIA/NemoClaw` | `7c721ae4d60fd54e11f4d0c7d0482ccd6ac8cded` |
| `linux/arm64` | `sha256:cbb5f8a11a17e5e5c7ce7499f3c6aff507bac15cc348310923436eb2e0f1536c` | `https://github.com/NVIDIA/NemoClaw` | `7c721ae4d60fd54e11f4d0c7d0482ccd6ac8cded` |
| Platform | Child manifest |
| --- | --- |
| `linux/amd64` | `sha256:da722766abb3c55d20242c3c62b434fd09583e4d92b5682aa98b374a74e05fa1` |
| `linux/arm64` | `sha256:c7fe8f5664beaf5f10ba990f1317d17a976ece1093a72f9efb0effdf93f3f48d` |

Each child manifest has the following per-platform Supply-chain Levels for Software Artifacts (SLSA) provenance:

| Platform | Attestation manifest | SLSA provenance layer | Builder ID |
| --- | --- | --- | --- |
| `linux/amd64` | `sha256:097d246b402e3483ddb408b9744c0e8db63a7cb33efcc781357d49c69fbee7b5` | `sha256:8e51f1fd6c647e30f1c2191862d013e7dbe07af28f4d29bcf09bb4770f04ba22` | `https://github.com/NVIDIA/NemoClaw/actions/runs/31636995117/attempts/1` |
| `linux/arm64` | `sha256:47ae417ac5e5c925674b727020ef1c5eaa5b1090bb862a7a2ed1c40ca7e79cf3` | `sha256:461eaf36474d1e02aed2acb60be69b37a4d464bc0805681d975522948ea258af` | `https://github.com/NVIDIA/NemoClaw/actions/runs/31636995117/attempts/1` |
| `linux/amd64` | `sha256:e64caf40f33b253c8952b09a4be9bb39c1adc8fd40bfff3f193accf8c722c49e` | `sha256:d1f23fb5dc32da33eae59caa719e331633867d6ed4c8e0662f6a372316314485` | `https://github.com/NVIDIA/NemoClaw/actions/runs/31717470863/attempts/1` |
| `linux/arm64` | `sha256:77a5dafd8d55132c2fb4ef7af213957b2eebd9b9bfaeb5e24121d6947c5a38d1` | `sha256:11dff2b05f47bc57315490c3483d896ea1b46dc31dd195c283af690ce718fba3` | `https://github.com/NVIDIA/NemoClaw/actions/runs/31717470863/attempts/1` |

Both in-toto layers use predicate type `https://slsa.dev/provenance/v1` and bind source `https://github.com/NVIDIA/NemoClaw` to revision `d243ea62509bae7832a23fe8636e947303c19c60`.

Both platform base-image jobs completed successfully.
BuildKit restored the package layers containing the exact `dpkg` assertions for `vim-common=2:9.2.0858-1`, `vim-tiny=2:9.2.0858-1`, and `libssh2-1t64=1.11.1-1+deb13u1+nemoclaw2` from cache.
The same run built and validated Hermes managed images from child manifests `sha256:da722766abb3c55d20242c3c62b434fd09583e4d92b5682aa98b374a74e05fa1` and `sha256:c7fe8f5664beaf5f10ba990f1317d17a976ece1093a72f9efb0effdf93f3f48d`.
Both `Validate exact managed image before promotion` steps completed successfully, confirming that the completed-image verifier passed with the selected base image.

The selected index supersedes the following historical base-image evidence:

Both in-toto layers use predicate type `https://slsa.dev/provenance/v1` and bind source `https://github.com/NVIDIA/NemoClaw` to revision `7c721ae4d60fd54e11f4d0c7d0482ccd6ac8cded`.
The selected index has no index-level attestation manifest.
Each platform attestation manifest contains only its SLSA provenance layer and has no software bill of materials (SBOM) layer.
| Evidence | Value |
| --- | --- |
| Producer run | `31636995117`, attempt 1, completed successfully |
| Source commit | `7c721ae4d60fd54e11f4d0c7d0482ccd6ac8cded` |
| OCI index | `sha256:4295138eb70f938189430f8dc7b3cd5db0aa762234e64e398a6a5ef60803126c` |

The superseded index resolved to the following platform evidence:

| Platform | Child manifest | Attestation manifest | SLSA provenance layer |
| --- | --- | --- | --- |
| `linux/amd64` | `sha256:f82972cf3d1497e60741ae0c48a870030d792e56652ee35868d30099cd93d831` | `sha256:097d246b402e3483ddb408b9744c0e8db63a7cb33efcc781357d49c69fbee7b5` | `sha256:8e51f1fd6c647e30f1c2191862d013e7dbe07af28f4d29bcf09bb4770f04ba22` |
| `linux/arm64` | `sha256:cbb5f8a11a17e5e5c7ce7499f3c6aff507bac15cc348310923436eb2e0f1536c` | `sha256:47ae417ac5e5c925674b727020ef1c5eaa5b1090bb862a7a2ed1c40ca7e79cf3` | `sha256:461eaf36474d1e02aed2acb60be69b37a4d464bc0805681d975522948ea258af` |

The selected build includes the exact-source dashboard WhatsApp session-path patch. Its amd64 and arm64 base-image builds passed the exact-source patch guard, locked bridge install, bridge-to-Baileys option assertions, and controlled-proxy WebSocket `CONNECT` regression.
The superseded build included the exact-source dashboard WhatsApp session-path patch and `libexpat1==2.8.3-1` for both supported architectures.
Its base-image builds passed the exact-source patch guard, locked bridge install, bridge-to-Baileys option assertions, and controlled-proxy WebSocket `CONNECT` regression.

The replacement follows the security-refreshed multi-platform index published by run `31006872948`, attempt 1, from source commit `bd668121e918e7b1dda13062bed728f18150360e`:
Earlier reviewed provenance includes the security-refreshed multi-platform index published by run `31006872948`, attempt 1, from source commit `bd668121e918e7b1dda13062bed728f18150360e`:

| Evidence | Value |
| --- | --- |
Expand Down Expand Up @@ -330,7 +353,7 @@ Each reviewed commit in the following table is an ancestor of `bd668121e918e7b1d
| `HERMES-9` | High | Pin and test | The selected Python delta adds no advisory regression, and the affected multipart parser is replaced with attested `0.0.32` plus hash and runtime probes. |
| `HERMES-10` | High | Pin and test | The exact-source patch updates Hermes metadata and its frozen lock together, selects `aiohttp==3.14.3`, `cryptography==50.0.0`, `mcp==1.28.1`, `Pillow==12.3.0`, `starlette==1.3.1`, and `tornado==6.5.7`, and fails the base image build on dependency inconsistency or installed-version drift. The `agents/hermes/Dockerfile` build checks `aiohttp==3.14.3` and `cryptography==50.0.0` in the Hermes sandbox image after messaging package installation. The check runs when `NEMOCLAW_MANAGED_IMAGE_CAPABILITY_UNION` is `0` or `1`. The base image separately checksum-pins Node.js `24.18.1` and checks uv `0.11.33`. |
| `HERMES-11` | High | Migrate, test, and runtime-proof | Root npm audit reports zero production findings, and the WhatsApp bridge removes the Baileys RC9 critical, high, and medium advisory entries. Both architectures still require native bridge and message-path evidence. |
| `HERMES-12` | High | Pin and runtime-proof | Trusted workflow run `30779271312`, attempt 1, built source commit `340c47857596e7cc347541a0b32fe9e24f201bcd` and published OCI index `sha256:956c3d0c812ee6caa56f3b6e307819925d920604adcf73c4a9e6229788967634`. Run `31006872948`, attempt 1, published security-refreshed index `sha256:57c091ab9b31c924eac0050e66c834c37df875154a254964302a31b119b50b96` from source commit `bd668121e918e7b1dda13062bed728f18150360e`, whose platform histories check `aiohttp==3.14.3` and `cryptography==50.0.0`. Trusted workflow run `31031662054`, attempt 1, rebuilt source commit `a7a7f3e470a75c404d316d2054445e16bb63b48c` with the exact-source dashboard WhatsApp session-path patch and published replacement OCI index `sha256:3d54b928baef9df403227e846f73079d13ca8424a27cd5268ca97bac3f030b27`. Run `31636995117`, attempt 1, rebuilt source commit `7c721ae4d60fd54e11f4d0c7d0482ccd6ac8cded` with `libexpat1==2.8.3-1` for amd64 and arm64 and published OCI index `sha256:4295138eb70f938189430f8dc7b3cd5db0aa762234e64e398a6a5ef60803126c`. The final Dockerfile pins that latest index. The amd64 and arm64 base-image builds in the reviewed runs passed the exact-source patch guard, locked bridge install, bridge-to-Baileys option assertions, and the controlled-proxy WebSocket `CONNECT` regression; the live final-image WhatsApp evidence is recorded under `HERMES-22`. |
| `HERMES-12` | High | Pin and runtime-proof | Trusted workflow run `30779271312`, attempt 1, built source commit `340c47857596e7cc347541a0b32fe9e24f201bcd` and published OCI index `sha256:956c3d0c812ee6caa56f3b6e307819925d920604adcf73c4a9e6229788967634`. Run `31006872948`, attempt 1, published security-refreshed index `sha256:57c091ab9b31c924eac0050e66c834c37df875154a254964302a31b119b50b96` from source commit `bd668121e918e7b1dda13062bed728f18150360e`, whose platform histories check `aiohttp==3.14.3` and `cryptography==50.0.0`. Trusted workflow run `31031662054`, attempt 1, rebuilt source commit `a7a7f3e470a75c404d316d2054445e16bb63b48c` with the exact-source dashboard WhatsApp session-path patch and published replacement OCI index `sha256:3d54b928baef9df403227e846f73079d13ca8424a27cd5268ca97bac3f030b27`. Run `31636995117`, attempt 1, rebuilt source commit `7c721ae4d60fd54e11f4d0c7d0482ccd6ac8cded` with `libexpat1==2.8.3-1` for amd64 and arm64 and published the superseded OCI index `sha256:4295138eb70f938189430f8dc7b3cd5db0aa762234e64e398a6a5ef60803126c`. Run `31717470863`, attempt 1, built source commit `d243ea62509bae7832a23fe8636e947303c19c60` and published OCI index `sha256:ffafa4dd1d8d5a802ae4fc4005b51e1accfa5e782e47de736a0d8d8bf2c83837`. Both platform base-image jobs completed successfully, with BuildKit restoring the package layers containing the exact Vim and libssh2 `dpkg` assertions from cache. The same run built and validated Hermes managed images from both exact child manifests, and both `Validate exact managed image before promotion` steps completed successfully. The `agents/hermes/Dockerfile` pins that index. The live final-image WhatsApp evidence is recorded under `HERMES-22`. |
| `HERMES-13` | Medium | Document bounded residual | Static `state_files` entries online-back up the default profile only. Cron or Discord ledgers created by a process launched under `profiles/<name>` remain in the raw `profiles` tar capture and can be inconsistent during a concurrent snapshot. Dynamic profile-local SQLite discovery is generic snapshot work outside this upgrade PR. |
| `HERMES-14` | High | Migrate and test | The browser evaluation denylist changed from default-on to opt-in. Generated configuration explicitly writes `browser.restrict_evaluate: true`, including when managed browser-gateway settings are merged, so the upgrade does not broaden page-context access. |
| `HERMES-15` | Medium | Migrate and test | The omitted gateway session-reset policy changed from bounded daily and idle expiry to no automatic reset. Generated configuration explicitly writes the complete outgoing reset and notification policy to preserve the retention bound without inheriting mutable dependency defaults. |
Expand Down Expand Up @@ -368,15 +391,16 @@ The review records the following source and test evidence.
The review records the following publication and registry evidence.

- Hermes CI run `29768400292`, PyPI publication run `29768427462`, and Docker publication run `29768440304` completed successfully.
- GitHub Actions workflow `.github/workflows/base-image.yaml` run `31636995117`, attempt 1, published the selected patched `linux/amd64` and `linux/arm64` base images and OCI index `sha256:4295138eb70f938189430f8dc7b3cd5db0aa762234e64e398a6a5ef60803126c`; run `31031662054`, attempt 1, published the superseded patched index, and run `31006872948`, attempt 1, published the preceding security-refreshed index.
- GitHub Actions workflow `.github/workflows/base-image.yaml` run `31717470863`, attempt 1, published the selected patched `linux/amd64` and `linux/arm64` base images and OCI index `sha256:ffafa4dd1d8d5a802ae4fc4005b51e1accfa5e782e47de736a0d8d8bf2c83837`; run `31636995117`, attempt 1, published the superseded index `sha256:4295138eb70f938189430f8dc7b3cd5db0aa762234e64e398a6a5ef60803126c`; run `31031662054`, attempt 1, published the preceding patched index; and run `31006872948`, attempt 1, published the security-refreshed index.
- Both platform base-image jobs in run `31717470863` completed successfully. BuildKit restored the package layers containing the exact `dpkg` assertions for `vim-common=2:9.2.0858-1`, `vim-tiny=2:9.2.0858-1`, and `libssh2-1t64=1.11.1-1+deb13u1+nemoclaw2` from cache.
- The same run built and validated Hermes managed images from both exact child manifests.
- Both `Validate exact managed image before promotion` steps completed successfully, confirming that the completed-image verifier passed with the selected base image.
- PyPI Trusted Publisher attestations bind both `hermes-agent==0.19.0` artifacts to source commit `3ef6bbd201263d354fd83ec55b3c306ded2eb72a`.
- The npm registry-integrity check matches the `hermes-agent==0.19.0` cross-check value recorded in this review.
- OCI inspection records the immutable index, both child manifests, image-configuration source and revision labels, per-platform SLSA provenance, and build histories.
- The selected index has no index-level attestation manifest, and neither platform attestation manifest contains an SBOM layer.
- OCI inspection records the immutable index, both child manifests, both attestation manifests, and their per-platform SLSA provenance layers.

Before merge, these checks must pass:

- The GitHub Actions Hermes sandbox image build from the patched base image OCI index must pass its installed-version checks, cron ledger relocation probe, and cross-identity probe.
- The managed MCP E2E test must pass discovery and invocation.
- The protected Hermes E2E tests must pass messaging, environment-credential rejection, restart, snapshot, rebuild, and rollback paths.
- Required repository checks and automated reviews must pass with no unresolved actionable finding.
Expand Down
2 changes: 1 addition & 1 deletion src/lib/agent/base-image-hermes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe("agent base image provisioning", () => {
);
expect(trackedRef).not.toBeNull();
expect(trackedRef?.[1]).toBe(
"ghcr.io/nvidia/nemoclaw/hermes-sandbox-base@sha256:4295138eb70f938189430f8dc7b3cd5db0aa762234e64e398a6a5ef60803126c",
"ghcr.io/nvidia/nemoclaw/hermes-sandbox-base@sha256:ffafa4dd1d8d5a802ae4fc4005b51e1accfa5e782e47de736a0d8d8bf2c83837",
);

const messagingInstallIndex = dockerfile.indexOf("RUN unset SSL_CERT_FILE REQUESTS_CA_BUNDLE");
Expand Down
7 changes: 7 additions & 0 deletions test/hermes-env-secret-boundary-hardening.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,13 @@ wait "$child"
});

describe("Hermes durable lazy-install target", () => {
it("accepts the provider-assigned Hermes API port in the runtime environment", () => {
const result = runRuntimeEnvValidation({ NEMOCLAW_HERMES_API_PORT: "8645" });

expect(result.status, result.stderr).toBe(0);
expect(result.stderr).toBe("");
});

it("accepts the image-owned lazy target in the runtime environment (#8613)", () => {
const result = runRuntimeEnvValidation({
HERMES_LAZY_INSTALL_TARGET: "/sandbox/.hermes/lazy-packages",
Expand Down
Loading