diff --git a/agents/hermes/Dockerfile b/agents/hermes/Dockerfile index fd25b8d97f6..113e8f9975e 100644 --- a/agents/hermes/Dockerfile +++ b/agents/hermes/Dockerfile @@ -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 @@ -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 diff --git a/agents/hermes/validate-env-secret-boundary.py b/agents/hermes/validate-env-secret-boundary.py index e1f43f0d6a7..d807c3869fa 100755 --- a/agents/hermes/validate-env-secret-boundary.py +++ b/agents/hermes/validate-env-secret-boundary.py @@ -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", diff --git a/docs/security/hermes-0.19.0-dependency-review.md b/docs/security/hermes-0.19.0-dependency-review.md index bc0e341d365..9fb54a61b91 100644 --- a/docs/security/hermes-0.19.0-dependency-review.md +++ b/docs/security/hermes-0.19.0-dependency-review.md @@ -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 @@ -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 | | --- | --- | @@ -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/` 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. | @@ -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. diff --git a/src/lib/agent/base-image-hermes.test.ts b/src/lib/agent/base-image-hermes.test.ts index c8b1a68a843..0f72bda7a33 100644 --- a/src/lib/agent/base-image-hermes.test.ts +++ b/src/lib/agent/base-image-hermes.test.ts @@ -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"); diff --git a/test/hermes-env-secret-boundary-hardening.test.ts b/test/hermes-env-secret-boundary-hardening.test.ts index b7cb310d591..1b98b6889ad 100644 --- a/test/hermes-env-secret-boundary-hardening.test.ts +++ b/test/hermes-env-secret-boundary-hardening.test.ts @@ -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",