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
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@
# ── CI / GitHub config ──
/.github/ @NVIDIA/nemoclaw-maintainer
/ci/ @NVIDIA/nemoclaw-maintainer
/ci/npm-audit-exceptions.json @NVIDIA/nemoclaw-security @NVIDIA/nemoclaw-maintainer
/scripts/lib/reviewed-npm-audit.mts @NVIDIA/nemoclaw-security @NVIDIA/nemoclaw-maintainer
2 changes: 2 additions & 0 deletions .github/workflows/base-image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ on:
- "agents/langchain-deepagents-code/requirements.lock"
- "agents/openclaw/mcporter-runtime/package.json"
- "agents/openclaw/mcporter-runtime/package-lock.json"
- "ci/npm-audit-exceptions.json"
# Dockerfile.base validates min_openclaw_version from this file at build time.
- "nemoclaw-blueprint/blueprint.yaml"
- "scripts/lib/openclaw-npm-remediation.mts"
- "scripts/lib/reviewed-npm-audit.mts"
- "scripts/lib/reviewed-npm-archive.mts"
- "scripts/checks/node-tar-image-scan.mts"
- "scripts/patch-bundled-npm-tar.mts"
Expand Down
18 changes: 15 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ COPY agents/openclaw/mcporter-runtime/package.json /usr/local/lib/nemoclaw/mcpor
COPY agents/openclaw/mcporter-runtime/package-lock.json /usr/local/lib/nemoclaw/mcporter-runtime/package-lock.json
COPY agents/openclaw/wechat-runtime/package.json /usr/local/lib/nemoclaw/wechat-runtime/package.json
COPY agents/openclaw/wechat-runtime/package-lock.json /usr/local/lib/nemoclaw/wechat-runtime/package-lock.json
COPY ci/npm-audit-exceptions.json /scripts/npm-audit-exceptions.json
COPY scripts/lib/reviewed-npm-archive.mts /scripts/lib/reviewed-npm-archive.mts
COPY scripts/lib/reviewed-npm-audit.mts /scripts/lib/reviewed-npm-audit.mts
COPY scripts/lib/openclaw-npm-remediation.mts /scripts/lib/openclaw-npm-remediation.mts
COPY scripts/patch-bundled-npm-tar.mts /scripts/patch-bundled-npm-tar.mts

Expand Down Expand Up @@ -315,14 +317,19 @@ RUN set -eu; \
MCPORTER_LOCK_SHA256="$(sha256sum /usr/local/lib/nemoclaw/mcporter-runtime/package-lock.json | awk '{print $1}')"; \
[ -n "$MCPORTER_LOCK_SHA256" ] \
|| { echo "ERROR: Could not hash the committed mcporter lockfile" >&2; exit 1; }; \
MCPORTER_AUDIT_POLICY_SHA256="$(sha256sum /scripts/npm-audit-exceptions.json | awk '{print $1}')"; \
MCPORTER_EXPECTED_AUDIT_EXCEPTIONS="$(node --experimental-strip-types --input-type=module -e \
'import fs from "node:fs"; import { parseAuditExceptionRegistry } from "/scripts/lib/reviewed-npm-audit.mts"; const policy=parseAuditExceptionRegistry(fs.readFileSync("/scripts/npm-audit-exceptions.json", "utf-8")); const ids=policy.exceptions.filter((entry)=>entry.graph==="mcporter-runtime").map((entry)=>entry.advisory).sort(); process.stdout.write(ids.join(",") || "none");')"; \
MCPORTER_EXPECTED_AUDIT_STATUS=clean; \
if [ "$MCPORTER_EXPECTED_AUDIT_EXCEPTIONS" != "none" ]; then MCPORTER_EXPECTED_AUDIT_STATUS=accepted-exceptions; fi; \
CUR_VER=$(openclaw --version 2>/dev/null | awk '{print $2}' || true); \
CUR_VER="${CUR_VER:-0.0.0}"; \
CUR_MCPORTER_VER=$(mcporter --version 2>/dev/null || true); \
CUR_MCPORTER_VER="${CUR_MCPORTER_VER:-0.0.0}"; \
OPENCLAW_PROVENANCE_PATH=/usr/local/share/nemoclaw/openclaw-base-provenance-v1; \
OPENCLAW_EXPECTED_PROVENANCE="$(mktemp)"; \
printf '%s\n' \
'schema=2' \
'schema=3' \
"package=openclaw@${OPENCLAW_VERSION}" \
"integrity=${EXPECTED_INTEGRITY}" \
"tarball=${EXPECTED_TARBALL}" \
Expand All @@ -331,7 +338,10 @@ RUN set -eu; \
"mcporter-integrity=${MCPORTER_EXPECTED_INTEGRITY}" \
"mcporter-tarball=${MCPORTER_EXPECTED_TARBALL}" \
"mcporter-lock-sha256=${MCPORTER_LOCK_SHA256}" \
'mcporter-recipe=locked-ci+audit-signatures-v1' \
"mcporter-audit-policy-sha256=${MCPORTER_AUDIT_POLICY_SHA256}" \
"mcporter-audit-status=${MCPORTER_EXPECTED_AUDIT_STATUS}" \
"mcporter-audit-exceptions=${MCPORTER_EXPECTED_AUDIT_EXCEPTIONS}" \
'mcporter-recipe=locked-ci+reviewed-audit+signatures-v2' \
> "$OPENCLAW_EXPECTED_PROVENANCE"; \
TRUSTED_BASE_IMAGE=0; \
case "$BASE_IMAGE" in \
Expand Down Expand Up @@ -400,7 +410,9 @@ RUN set -eu; \
'const { StreamableHTTPServerTransport } = await import("file:///usr/local/lib/nemoclaw/mcporter-runtime/node_modules/@modelcontextprotocol/sdk/dist/esm/server/streamableHttp.js"); const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined }); await transport.close();'; \
ln -s /usr/local/lib/nemoclaw/mcporter-runtime/node_modules/.bin/mcporter /usr/local/bin/mcporter; \
test "$(mcporter --version)" = "$MCPORTER_VERSION"; \
npm --prefix /usr/local/lib/nemoclaw/mcporter-runtime audit --omit=dev --audit-level=low; \
node --experimental-strip-types /scripts/lib/reviewed-npm-audit.mts \
--directory /usr/local/lib/nemoclaw/mcporter-runtime \
--exceptions /scripts/npm-audit-exceptions.json --graph mcporter-runtime --threshold high; \
npm --prefix /usr/local/lib/nemoclaw/mcporter-runtime audit signatures; \
fi

Expand Down
19 changes: 16 additions & 3 deletions Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,9 @@ ARG MCPORTER_0_7_3_INTEGRITY=sha512-egoPVYqTnWb3NjRIxo+xc8OrAI0dlPrJm9pAiZx0pImu
ARG MCPORTER_0_7_3_TARBALL=https://registry.npmjs.org/mcporter/-/mcporter-0.7.3.tgz
COPY agents/openclaw/mcporter-runtime/package.json /usr/local/lib/nemoclaw/mcporter-runtime/package.json
COPY agents/openclaw/mcporter-runtime/package-lock.json /usr/local/lib/nemoclaw/mcporter-runtime/package-lock.json
COPY ci/npm-audit-exceptions.json /scripts/npm-audit-exceptions.json
COPY scripts/lib/reviewed-npm-archive.mts /scripts/lib/reviewed-npm-archive.mts
COPY scripts/lib/reviewed-npm-audit.mts /scripts/lib/reviewed-npm-audit.mts
COPY scripts/lib/openclaw-npm-remediation.mts /scripts/lib/openclaw-npm-remediation.mts
COPY scripts/patch-bundled-npm-tar.mts /scripts/patch-bundled-npm-tar.mts

Expand Down Expand Up @@ -324,16 +326,23 @@ RUN --mount=type=bind,source=nemoclaw-blueprint/blueprint.yaml,target=/tmp/bluep
'const { StreamableHTTPServerTransport } = await import("file:///usr/local/lib/nemoclaw/mcporter-runtime/node_modules/@modelcontextprotocol/sdk/dist/esm/server/streamableHttp.js"); const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined }); await transport.close();' \
&& ln -s /usr/local/lib/nemoclaw/mcporter-runtime/node_modules/.bin/mcporter /usr/local/bin/mcporter \
&& test "$(mcporter --version)" = "$MCPORTER_VERSION" \
&& npm --prefix /usr/local/lib/nemoclaw/mcporter-runtime audit --omit=dev --audit-level=low \
&& node --experimental-strip-types /scripts/lib/reviewed-npm-audit.mts \
--directory /usr/local/lib/nemoclaw/mcporter-runtime \
--exceptions /scripts/npm-audit-exceptions.json --graph mcporter-runtime --threshold high \
--report /tmp/mcporter-npm-audit.json --result /tmp/mcporter-npm-audit-policy.json \
&& npm --prefix /usr/local/lib/nemoclaw/mcporter-runtime audit signatures \
&& MCPORTER_AUDIT_STATUS="$(node -p "require('/tmp/mcporter-npm-audit-policy.json').status")" \
&& MCPORTER_AUDIT_EXCEPTIONS="$(node -p "require('/tmp/mcporter-npm-audit-policy.json').acceptedAdvisories.join(',') || 'none'")" \
&& MCPORTER_AUDIT_POLICY_SHA256="$(node -p "require('/tmp/mcporter-npm-audit-policy.json').exceptionPolicySha256")" \
&& test -n "$MCPORTER_AUDIT_STATUS" -a -n "$MCPORTER_AUDIT_EXCEPTIONS" -a -n "$MCPORTER_AUDIT_POLICY_SHA256" \
&& MCPORTER_LOCK_SHA256="$(sha256sum /usr/local/lib/nemoclaw/mcporter-runtime/package-lock.json | awk '{print $1}')" \
&& test -n "$MCPORTER_LOCK_SHA256" \
&& OPENCLAW_PROVENANCE_PATH=/usr/local/share/nemoclaw/openclaw-base-provenance-v1 \
&& OPENCLAW_PROVENANCE_DIR="$(dirname "$OPENCLAW_PROVENANCE_PATH")" \
&& mkdir -p "$OPENCLAW_PROVENANCE_DIR" \
&& OPENCLAW_PROVENANCE_TMP="$(mktemp "${OPENCLAW_PROVENANCE_PATH}.tmp.XXXXXX")" \
&& printf '%s\n' \
'schema=2' \
'schema=3' \
"package=openclaw@${OPENCLAW_VERSION}" \
"integrity=${EXPECTED_INTEGRITY}" \
"tarball=${EXPECTED_TARBALL}" \
Expand All @@ -342,10 +351,14 @@ RUN --mount=type=bind,source=nemoclaw-blueprint/blueprint.yaml,target=/tmp/bluep
"mcporter-integrity=${MCPORTER_EXPECTED_INTEGRITY}" \
"mcporter-tarball=${MCPORTER_EXPECTED_TARBALL}" \
"mcporter-lock-sha256=${MCPORTER_LOCK_SHA256}" \
'mcporter-recipe=locked-ci+audit-signatures-v1' \
"mcporter-audit-policy-sha256=${MCPORTER_AUDIT_POLICY_SHA256}" \
"mcporter-audit-status=${MCPORTER_AUDIT_STATUS}" \
"mcporter-audit-exceptions=${MCPORTER_AUDIT_EXCEPTIONS}" \
'mcporter-recipe=locked-ci+reviewed-audit+signatures-v2' \
> "$OPENCLAW_PROVENANCE_TMP" \
&& chmod 0444 "$OPENCLAW_PROVENANCE_TMP" \
&& mv -f "$OPENCLAW_PROVENANCE_TMP" "$OPENCLAW_PROVENANCE_PATH" \
&& rm -f /tmp/mcporter-npm-audit.json /tmp/mcporter-npm-audit-policy.json \
&& pip3 install --no-cache-dir --break-system-packages "pyyaml==6.0.3"


Expand Down
7 changes: 4 additions & 3 deletions agents/openclaw/dependency-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ Update it and `agents/openclaw/mcporter-runtime/package*.json` together whenever
- Registry metadata independently queried from npm: 2026-06-30.
- Locked graph: `agents/openclaw/mcporter-runtime/package-lock.json` (npm lockfile version 3).
- Lock regeneration command: `npm --prefix agents/openclaw/mcporter-runtime install --package-lock-only --ignore-scripts --omit=dev`
- Advisory command: `npm --prefix agents/openclaw/mcporter-runtime ci --ignore-scripts --omit=dev && npm --prefix agents/openclaw/mcporter-runtime audit --omit=dev --audit-level=low && npm --prefix agents/openclaw/mcporter-runtime audit signatures`
- Advisory command: `npm --prefix agents/openclaw/mcporter-runtime ci --ignore-scripts --omit=dev && node --experimental-strip-types scripts/lib/reviewed-npm-audit.mts --directory agents/openclaw/mcporter-runtime --exceptions ci/npm-audit-exceptions.json --graph mcporter-runtime --threshold high && npm --prefix agents/openclaw/mcporter-runtime audit signatures`
- Advisory review date: 2026-07-21.
- Advisory result: `0` known vulnerabilities across the resolved production dependency graph; npm verified registry signatures for all `120` resolved packages and attestations for `13` packages.
- Security override: `@hono/node-server@2.0.11` (`sha512-bjD221KPLoJTWUwso1J6fGKiTXEUFedG/s0visavY4zakFPkeGURMRNly+FhBHs7T8Dz4qHaZIMX9ZoJHSJtKA==`) replaces the SDK's vulnerable `1.19.14` resolution for `GHSA-frvp-7c67-39w9` and the previously reviewed `2.0.5` resolution affected by `GHSA-9mqv-5hh9-4cgg`. `2.0.5` is the first patched release for `GHSA-frvp-7c67-39w9`. The reviewed v2 range retains the `getRequestListener` API used by `@modelcontextprotocol/sdk`; its Node.js 20 floor is below NemoClaw's Node.js 22.19 floor, and the `/vercel` adapter is not consumed. Mcporter's production path imports the SDK's client transport, not the server adapter, and the image build still exercises the installed CLI after the locked install. Remove the override when the SDK's declared range resolves to a reviewed release outside both affected ranges.
- Security override: `fast-uri@3.1.4` (`sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==`) replaces Ajv's vulnerable `3.1.3` resolution for `GHSA-v2hh-gcrm-f6hx`. It remains within Ajv's declared `^3.0.1` range and preserves the reviewed v3 API boundary. Remove the override when the declared graph resolves to a reviewed patched release.

Both image paths install the committed graph with `npm ci --ignore-scripts --omit=dev` because the published package declares no install-time lifecycle script and NemoClaw needs only its already-built CLI.
The reviewed audit wrapper reports lower-severity production findings and blocks unaccepted high or critical advisories. The default `ci/npm-audit-exceptions.json` registry is empty. Any future exception must match one advisory, graph, package, installed version, and severity; identify an owner and NemoClaw tracking issue; state a decision, rationale, and expiry no more than 30 days away; and include compensating controls for temporary risk acceptance. Missing, malformed, expired, overlong, mismatched, or unused exceptions fail closed. The repository-wide audit also rejects exceptions for unknown graph IDs. Registry signature verification remains a separate control.

## WeChat plugin runtime graph

Expand All @@ -41,7 +42,7 @@ The lock records the exact version, registry URL, and integrity for every transi
## Source-of-Truth Boundary

- `invalidState`: the image installs a package graph, tarball, license, or advisory state that differs from the independently queried npm registry records for `mcporter@0.7.3`, resolves `@hono/node-server` to any version other than exact `2.0.11`, or resolves `fast-uri` to any version other than exact `3.1.4`.
- `sourceBoundary`: npm owns registry metadata, tarball integrity, provenance signatures, and advisory responses; NemoClaw owns the exact lock, script-disabled install, Docker integrity assertion, and review record.
- `sourceBoundary`: npm owns registry metadata, tarball integrity, provenance signatures, and advisory responses; NemoClaw owns the exact lock, script-disabled install, Docker integrity assertion, empty-by-default audit exception registry, and review record.
- `whyNotSourceFix`: a repository note cannot make external registry state trustworthy, so image builds execute `npm audit` and `npm audit signatures` against the locked production graph and reviewers compare the lock with the registry response.
- `regressionTest`: `test/mcporter-supply-chain.test.ts` keeps the version, integrity, lock metadata, Docker install flags, audit commands, and this review synchronized.
- `regressionTest`: `test/mcporter-supply-chain.test.ts` keeps the version, integrity, lock metadata, Docker install flags, audit commands, and this review synchronized; `test/reviewed-npm-audit.test.ts` proves exact matching and fail-closed exception validation.
- `removalCondition`: remove this runtime dependency and review when OpenClaw provides the required authenticated Streamable HTTP client lifecycle without mcporter, or repeat the independent review for a newly pinned version.
4 changes: 4 additions & 0 deletions ci/npm-audit-exceptions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"schemaVersion": 1,
"exceptions": []
}
5 changes: 4 additions & 1 deletion ci/reviewed-npm-audit.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"schemaVersion": 1,
"schemaVersion": 2,
"nodeVersion": "22.23.1",
"severityThreshold": "high",
"exceptionFile": "ci/npm-audit-exceptions.json",
"archiveGraphId": "reviewed-archive-graph",
"artifactDirectory": "coverage/reviewed-npm-audit",
"archivePackages": [
{
Expand Down Expand Up @@ -61,6 +63,7 @@
],
"lockedGraphs": [
{
"id": "mcporter-runtime",
"label": "mcporter 0.7.3 locked runtime graph",
"packageSpec": "mcporter@0.7.3",
"integrity": "sha512-egoPVYqTnWb3NjRIxo+xc8OrAI0dlPrJm9pAiZx0pImuNIV5rKhGtTnIfH/Y1ldGPVu74ibj3KR5c9U/QSdQFA==",
Expand Down
Loading
Loading