Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
6268385
fix: carry reviewed npm audit into protected builds
sandl99 Sep 7, 2026
ddca63d
refactor: narrow protected audit fix
sandl99 Sep 7, 2026
3d4672a
fix: order audit response type guards
sandl99 Sep 7, 2026
db2b598
fix: carry audit through protected cache seed
sandl99 Sep 7, 2026
a88afed
test: model protected audit verification helper
sandl99 Sep 7, 2026
46f28b5
fix(security): pin protected audit evidence paths
sandl99 Sep 7, 2026
fcd1873
Merge branch 'main' into fix/npm-audit-incomplete-11088
sandl99 Sep 8, 2026
cd32fc1
refactor: reduce protected audit handoff
sandl99 Sep 8, 2026
be13769
fix: preserve audit evidence in protected cache
sandl99 Sep 8, 2026
38ee435
test: stage protected audit verifier in fixtures
sandl99 Sep 8, 2026
5eaf8f5
Merge branch 'main' into fix/npm-audit-incomplete-11088
sandl99 Sep 8, 2026
d4c2731
Merge branch 'main' into fix/npm-audit-incomplete-11088
sandl99 Sep 8, 2026
cd3679e
fix(security): preserve protected audit isolation
rsliter Sep 8, 2026
08bb2e2
fix(images): preserve audit RUN parsing
rsliter Sep 8, 2026
8ac4d29
merge(security): refresh protected audit fix
rsliter Sep 8, 2026
253f247
fix(images): clean failed protected cache exports
rsliter Sep 8, 2026
5073932
test(security): verify protected audit handoff
rsliter Sep 8, 2026
bdf27c8
test(security): keep audit handoff setup direct
rsliter Sep 8, 2026
e7b9017
test(security): rely on audit verifier bindings
rsliter Sep 8, 2026
31d6876
merge(security): refresh protected audit handoff
rsliter Sep 8, 2026
ff73c97
fix(images): retry reviewed audit export
rsliter Sep 8, 2026
6a64064
merge(images): refresh reviewed audit export
rsliter Sep 8, 2026
20e04a2
merge(images): refresh reviewed audit export
rsliter Sep 8, 2026
b9b31a0
fix(security): reject build-context audit evidence
rsliter Sep 8, 2026
a5a7bd5
test(images): detect changed audit receipt
rsliter Sep 8, 2026
f479884
merge: refresh PR 11156 onto main
rsliter Sep 8, 2026
5d2782c
fix(audit): retry refused registry connections
rsliter Sep 8, 2026
4a8ab51
merge: refresh PR 11156 onto main
rsliter Sep 8, 2026
451ca9b
refactor(audit): share mcporter evidence verification
rsliter Sep 8, 2026
e433dfc
test(images): retain base audit outputs
rsliter Sep 8, 2026
3cfbff6
merge: refresh PR 11156 onto main
rsliter Sep 8, 2026
bdd9385
test(audit): execute protected mcporter handoff
rsliter Sep 8, 2026
fdb356e
merge: refresh PR #11156 with main
rsliter Sep 8, 2026
e165162
test(audit): keep producer handoff coverage lean
rsliter Sep 8, 2026
2512f82
merge: refresh PR #11156 with main
rsliter Sep 9, 2026
1871df2
merge: refresh PR #11156 with main
rsliter Sep 9, 2026
b6dce0c
fix(images): diagnose empty audit receipt hashes
rsliter Sep 9, 2026
ef37a0a
fix(e2e): run protected cache consumer from candidate
rsliter Sep 9, 2026
f946900
fix(ci): isolate candidate image build from docker auth
rsliter Sep 9, 2026
4ba522b
test(audit): canonicalize handoff temp path
jyaunches Sep 9, 2026
070c6a0
fix(ci): preserve trusted protected controller
rsliter Sep 9, 2026
8a8d335
merge: preserve concurrent PR #11156 fix
rsliter Sep 9, 2026
3f56655
merge: refresh PR #11156 with main
rsliter Sep 9, 2026
07459b1
fix(ci): trust protected npm audit handoff
rsliter Sep 9, 2026
c3a9682
Merge remote-tracking branch 'origin/main' into refresh-pr11156
rsliter Sep 9, 2026
1fc8653
docs(audit): describe trusted protected producer
rsliter Sep 9, 2026
897354b
refactor(ci): inline audit evidence input
rsliter Sep 9, 2026
cb44beb
fix(ci): remove stale audit state
rsliter Sep 9, 2026
e8531ca
Merge branch 'main' into fix/npm-audit-incomplete-11088
cv Sep 9, 2026
a0120b0
Merge remote-tracking branch 'origin/main' into fix/npm-audit-incompl…
jyaunches Sep 9, 2026
116d974
fix(ci): align protected audit workflow gates
jyaunches Sep 9, 2026
ab18eac
fix(ci): reuse audit evidence before protected builds
sandl99 Sep 9, 2026
40c2554
fix(ci): scope reviewed audit cache identity
jyaunches Sep 9, 2026
177f497
merge: integrate latest PR repairs
jyaunches Sep 9, 2026
c30ac05
Merge remote-tracking branch 'origin/main' into fix/npm-audit-incompl…
jyaunches Sep 9, 2026
8ab0aa4
test(ci): verify paired audit evidence handoff
jyaunches Sep 9, 2026
a638f70
Merge remote-tracking branch 'origin/main' into fix/npm-audit-incompl…
jyaunches Sep 9, 2026
663d7cf
fix(ci): centralize protected audit authority
jyaunches Sep 9, 2026
069cfa2
merge: integrate latest main into audit fix
rsliter Sep 9, 2026
1f9c0bb
fix(ci): refresh plugin cache seed identity
rsliter Sep 9, 2026
d92eff4
fix(audit): retry empty transport responses
rsliter Sep 9, 2026
284b0ce
merge: resolve conflicts with main
github-actions[bot] Sep 9, 2026
47708ed
test(security): isolate forged audit result
rsliter Sep 9, 2026
fbc4312
merge: prepare trusted conflict resolution
rsliter Sep 9, 2026
307ddd9
Merge branch 'main' into fix/npm-audit-incomplete-11088
rsliter Sep 9, 2026
65893ca
merge: restore reviewed protected audit resolution
rsliter Sep 9, 2026
320d57d
merge: prepare trusted conflict resolution
rsliter Sep 9, 2026
dbe9f0e
merge: integrate current main
rsliter Sep 9, 2026
7b3d841
merge: restore reviewed protected audit resolution
rsliter Sep 9, 2026
1b2805e
test(security): align audit fixture with Node 22
rsliter Sep 9, 2026
a6aead6
fix(ci): complete reviewed audit follow-up
jyaunches Sep 9, 2026
a8789d8
merge: integrate current main
jyaunches Sep 9, 2026
d0b70f7
test(security): align audit fixture with Node 22
rsliter Sep 9, 2026
2a88fc3
fix(build): tolerate omitted audit hash args
jyaunches Sep 9, 2026
16af7c9
Merge branch 'main' into fix/npm-audit-incomplete-11088
rsliter Sep 9, 2026
cf181d4
docs(audit): split handoff trust boundaries
jyaunches Sep 9, 2026
98f93c4
merge: integrate latest main
jyaunches Sep 9, 2026
3b1c655
merge: integrate current main
jyaunches Sep 9, 2026
c8f51be
merge: preserve protected PR history
jyaunches Sep 9, 2026
4b3130d
Merge branch 'main' into fix/npm-audit-incomplete-11088
rsliter Sep 9, 2026
91f49ab
Merge branch 'main' into fix/npm-audit-incomplete-11088
rsliter Sep 9, 2026
d59be95
Merge branch 'main' into fix/npm-audit-incomplete-11088
rsliter Sep 10, 2026
aa9075d
merge(ci): integrate trusted protected audit handoff
sandl99 Sep 10, 2026
73b67a6
merge(ci): consume published base and isolate vLLM fixtures
sandl99 Sep 10, 2026
71dd288
merge: integrate published main for protected image evidence
sandl99 Sep 10, 2026
a126c3f
merge: refresh PR #11156 onto current main
rsliter Sep 10, 2026
0d8a071
merge: include latest main in PR #11156
rsliter Sep 10, 2026
733c00a
Merge branch 'main' into fix/npm-audit-incomplete-11088
rsliter Sep 10, 2026
23a959a
Merge branch 'main' into fix/npm-audit-incomplete-11088
rsliter Sep 10, 2026
e0e1f71
Merge branch 'main' into fix/npm-audit-incomplete-11088
rsliter Sep 10, 2026
d364d1b
Merge branch 'main' into fix/npm-audit-incomplete-11088
rsliter Sep 10, 2026
0a17166
Merge branch 'main' into fix/npm-audit-incomplete-11088
rsliter Sep 10, 2026
736414d
Merge branch 'main' into fix/npm-audit-incomplete-11088
rsliter Sep 10, 2026
7268da9
merge: refresh PR #11156 onto main
rsliter Sep 10, 2026
063bfea
merge: include latest main in PR #11156
rsliter Sep 10, 2026
622b780
merge: include current main in PR #11156
rsliter Sep 10, 2026
c0e5a73
fix(ci): use current audit verifier flag
rsliter Sep 10, 2026
3747466
merge: refresh PR #11156 onto current main
rsliter Sep 10, 2026
1441df8
merge: include latest main in PR #11156
rsliter Sep 10, 2026
fb2c91e
fix(security): bind protected audit evidence
rsliter Sep 10, 2026
ffb23b1
fix(security): preserve shell-only audit verification
rsliter Sep 10, 2026
ace89a7
fix(security): parse cached audit receipts strictly
rsliter Sep 11, 2026
31645fc
style(test): apply audit handoff formatting
rsliter Sep 11, 2026
abbed15
fix(security): trim protected audit handoff
sandl99 Sep 11, 2026
86ca177
Merge branch 'main' into fix/npm-audit-incomplete-11088
sandl99 Sep 11, 2026
1bd6e08
merge: use published base for protected GPU qualification
sandl99 Sep 11, 2026
38fa1eb
test: isolate vLLM profile selection from host probes
sandl99 Sep 11, 2026
ca72fcb
test: retain bounded gateway diagnostics for activation failures
sandl99 Sep 11, 2026
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
28 changes: 10 additions & 18 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@
COPY scripts/lib/reviewed-npm-audit.mts /scripts/lib/reviewed-npm-audit.mts
COPY scripts/lib/npm-audit-receipt.mts /scripts/lib/npm-audit-receipt.mts
COPY scripts/lib/openclaw-npm-remediation.mts /scripts/lib/openclaw-npm-remediation.mts
COPY scripts/lib/verify-mcporter-audit.sh /scripts/lib/verify-mcporter-audit.sh
COPY scripts/patch-bundled-npm-brace-expansion.mts /scripts/patch-bundled-npm-brace-expansion.mts
COPY scripts/lib/patch-bundled-npm-ip-address.mts /scripts/lib/patch-bundled-npm-ip-address.mts
COPY scripts/patch-bundled-npm-tar.mts /scripts/patch-bundled-npm-tar.mts
Expand Down Expand Up @@ -599,6 +600,13 @@
COPY nemoclaw-blueprint/openclaw-plugins/ /usr/local/share/nemoclaw/openclaw-plugins/
COPY --from=mcp-tool-discovery-runtime /opt/mcp-tool-discovery-runtime/dist/ /usr/local/lib/nemoclaw/mcp-tool-discovery-runtime/

# Keep optional audit evidence in an uncommitted mount source. The checked-in
# seed has no reviewed-npm-audit directory, so ordinary builds still perform a
# live audit. Protected builders import validated evidence into the seed before
# invoking this Dockerfile with network access disabled.
FROM scratch AS protected-mcporter-audit-cache
COPY tools/mcp-tool-discovery-runtime/npm-cache-seed/ /seed/

# Stage 3: Runtime image — pull cached base from GHCR
# hadolint ignore=DL3006
FROM ${BASE_IMAGE}
Expand Down Expand Up @@ -823,6 +831,7 @@
RUN --network=default \
--mount=type=secret,id=nemoclaw-mcporter-audit-receipt,required=false \
--mount=type=secret,id=nemoclaw-mcporter-audit-raw-report,required=false \
--mount=type=bind,from=protected-mcporter-audit-cache,source=/seed,target=/run/nemoclaw-mcporter-audit-cache \
set -eu; \
if [ -f /usr/local/share/nemoclaw/corporate-ca.pem ]; then \
export CURL_CA_BUNDLE=/usr/local/share/nemoclaw/corporate-ca.pem; \
Expand Down Expand Up @@ -986,24 +995,7 @@
ln -s /usr/local/lib/nemoclaw/mcporter-runtime/node_modules/.bin/mcporter /usr/local/bin/mcporter; \
test "$(mcporter --version)" = "$MCPORTER_VERSION"; \
fi; \
MCPORTER_RECEIPT=/run/secrets/nemoclaw-mcporter-audit-receipt; \
MCPORTER_RAW_REPORT=/run/secrets/nemoclaw-mcporter-audit-raw-report; \
if [ -f "$MCPORTER_RECEIPT" ] || [ -f "$MCPORTER_RAW_REPORT" ] || [ -n "${NEMOCLAW_MCPORTER_AUDIT_RECEIPT_SHA256:-}" ]; then \
[ -f "$MCPORTER_RECEIPT" ] && [ -f "$MCPORTER_RAW_REPORT" ] && printf %s "$NEMOCLAW_MCPORTER_AUDIT_RECEIPT_SHA256" | grep -qxE '[0-9a-f]{64}' \
|| { echo "ERROR: cached mcporter audit requires paired receipt, raw report, and receipt SHA-256" >&2; exit 1; }; \
printf '%s %s\n' "$NEMOCLAW_MCPORTER_AUDIT_RECEIPT_SHA256" "$MCPORTER_RECEIPT" | sha256sum -c -; \
node --experimental-strip-types /scripts/lib/npm-audit-receipt.mts \
--receipt "$MCPORTER_RECEIPT" \
--package-json /usr/local/lib/nemoclaw/mcporter-runtime/package.json \
--package-lock /usr/local/lib/nemoclaw/mcporter-runtime/package-lock.json \
--raw-report "$MCPORTER_RAW_REPORT" --exceptions /scripts/npm-audit-exceptions.json \
--graph mcporter-runtime --audit-config /scripts/reviewed-npm-audit.json \
--registry https://registry.yarnpkg.com --threshold high --legacy-npmjs true; \
else \
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; \
fi
bash /scripts/lib/verify-mcporter-audit.sh

# Patch OpenClaw media fetch for proxy-only sandbox (NVIDIA/NemoClaw#1755).
#
Expand Down Expand Up @@ -1575,10 +1567,10 @@
# CHAT_UI_URL is a non-loopback address (Brev Launchable, remote deployments)
# since terminal-based pairing is impossible in those contexts.
# Default: "0" (device auth enabled for local deployments — secure by default).
ARG NEMOCLAW_DISABLE_DEVICE_AUTH=0

Check warning on line 1570 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "NEMOCLAW_DISABLE_DEVICE_AUTH") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 1570 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "NEMOCLAW_DISABLE_DEVICE_AUTH") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
# Internal audit provenance for the opt-out above. Standard onboarding rewrites
# this to managed-onboard; direct image builders retain operator provenance.
ARG NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE=operator

Check warning on line 1573 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 1573 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
# Compatibility build arg for older custom Dockerfiles and rebuild tooling.
# NemoClaw-managed images intentionally do not consume it; gateway auth tokens
# are generated at container startup and are never baked into image layers.
Expand Down Expand Up @@ -1612,7 +1604,7 @@
# NEMOCLAW_MESSAGING_PLAN_B64 intentionally remains ARG-only: Docker exposes it
# to build RUN processes without retaining the full plan in the final image env.
# Direct ARG interpolation into inline source is a code injection vector (C-2).
ENV NEMOCLAW_MODEL=${NEMOCLAW_MODEL} \

Check warning on line 1607 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 1607 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "NEMOCLAW_DISABLE_DEVICE_AUTH") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 1607 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 1607 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "NEMOCLAW_DISABLE_DEVICE_AUTH") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
NEMOCLAW_INFERENCE_PROVIDER_ID=${NEMOCLAW_INFERENCE_PROVIDER_ID} \
NEMOCLAW_UPSTREAM_PROVIDER=${NEMOCLAW_UPSTREAM_PROVIDER} \
NEMOCLAW_PRIMARY_MODEL_REF=${NEMOCLAW_PRIMARY_MODEL_REF} \
Expand Down
31 changes: 31 additions & 0 deletions Dockerfile.protected-npm-audit
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Produce reviewed mcporter audit evidence on the networked managed-image
# builder so the protected rebuild can stay fully offline.
FROM node:22-trixie-slim@sha256:db8a96a63e5264607ada2d206758876ebbed6a12be2ada7517793cbfb0c2a29c AS protected-mcporter-audit
ENV RUNNER_TEMP=/tmp
WORKDIR /opt/nemoclaw-audit
COPY .github/actions/ci-reviewed-npm-audit/verify-and-install-npm.sh .github/actions/ci-reviewed-npm-audit/verify-and-install-npm.sh
COPY ci/npm-audit-exceptions.json ci/reviewed-npm-audit.json ci/
COPY scripts/audit-reviewed-npm-graph.mts scripts/audit-reviewed-npm-graph.mts
COPY scripts/lib/npm-audit-receipt.mts scripts/lib/openclaw-npm-remediation.mts scripts/lib/repository-input-path.mts scripts/lib/reviewed-npm-archive.mts scripts/lib/reviewed-npm-audit.mts scripts/lib/
COPY agents/openclaw/mcporter-runtime/package.json agents/openclaw/mcporter-runtime/package-lock.json agents/openclaw/mcporter-runtime/
# hadolint ignore=DL3016,DL4006,SC2155
RUN --network=default set -eu; \
export NEMOCLAW_REVIEWED_NPM_VERSION="$(node -p "require('./ci/reviewed-npm-audit.json').npmVersion")"; \
export NEMOCLAW_REVIEWED_NPM_INTEGRITY="$(node -p "require('./ci/reviewed-npm-audit.json').npmIntegrity")"; \
env -u NODE_AUTH_TOKEN -u NPM_TOKEN -u NPM_CONFIG__AUTH_TOKEN \
bash .github/actions/ci-reviewed-npm-audit/verify-and-install-npm.sh; \
test "$(npm --version)" = "$NEMOCLAW_REVIEWED_NPM_VERSION"
RUN --network=default env \
-u NODE_AUTH_TOKEN -u NPM_TOKEN -u NPM_CONFIG__AUTH_TOKEN \
NEMOCLAW_REVIEWED_NPM_AUDIT_LOCKED_GRAPH=mcporter-runtime \
NEMOCLAW_REVIEWED_NPM_AUDIT_REPORT_DIR=artifacts/reviewed-npm-audit \
NPM_CONFIG_REGISTRY=https://registry.npmjs.org/ \
NPM_CONFIG_USERCONFIG=/dev/null \
node --experimental-strip-types scripts/audit-reviewed-npm-graph.mts

FROM scratch AS protected-mcporter-audit-evidence
COPY --from=protected-mcporter-audit --chmod=0400 /opt/nemoclaw-audit/artifacts/reviewed-npm-audit/mcporter-runtime.receipt.json /mcporter-runtime.receipt.json
COPY --from=protected-mcporter-audit --chmod=0400 /opt/nemoclaw-audit/artifacts/reviewed-npm-audit/mcporter-runtime.raw.json /mcporter-runtime.raw.json
51 changes: 51 additions & 0 deletions scripts/audit-reviewed-npm-graph.mts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ type ReviewedAuditReport = Readonly<{
threshold?: Severity;
}>;

export function selectLockedGraph(
config: Readonly<{ lockedGraphs: readonly LockedGraph[] }>,
graphId: string | undefined,
): Readonly<{ graph: LockedGraph; index: number }> | undefined {
if (!graphId) return undefined;
const index = config.lockedGraphs.findIndex((graph) => graph.id === graphId);
if (index < 0) throw new Error("reviewed npm audit locked graph is not configured");
return { graph: config.lockedGraphs[index]!, index };
}

const TRUSTED_REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const TARGET_REPO_ROOT = fs.realpathSync(
path.resolve(process.env.NEMOCLAW_REVIEWED_NPM_AUDIT_TARGET_ROOT ?? TRUSTED_REPO_ROOT),
Expand Down Expand Up @@ -873,6 +883,10 @@ export function assertReviewedAuditReportsPass(

function main(): void {
const config = readConfig();
const selectedLockedGraph = selectLockedGraph(
config,
process.env.NEMOCLAW_REVIEWED_NPM_AUDIT_LOCKED_GRAPH,
);
const expectedNode = `v${config.nodeVersion}`;
if (process.version !== expectedNode) {
throw new Error(`reviewed npm audit requires Node ${expectedNode}; running ${process.version}`);
Expand Down Expand Up @@ -901,6 +915,43 @@ function main(): void {
}
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-reviewed-npm-audit-"));
try {
if (selectedLockedGraph) {
const { graph, index } = selectedLockedGraph;
const result = auditLockedGraph(
graph,
index,
config,
tempRoot,
exceptionFile,
artifactDirectory,
npmVersion,
);
assertReviewedAuditReportsPass(
[{ label: graph.label, threshold: graph.severityThreshold, result }],
config.severityThreshold,
);
emitAuditReceipt({
artifactDirectory,
graphId: graph.id,
npmVersion,
packageJsonFile: targetRepositoryPath(
path.join(graph.directory, "package.json"),
`${graph.label} package manifest`,
),
packageLockFile: targetRepositoryPath(
path.join(graph.directory, "package-lock.json"),
`${graph.label} lockfile`,
),
rawReportFile: path.join(
artifactDirectory,
`locked-graph-${index + 1}.json`,
),
registryOrigin: NPM_AUDIT_REGISTRY,
result,
threshold: graph.severityThreshold ?? config.severityThreshold,
});
return;
}
const sourceResult = auditSourceGraph(
config,
tempRoot,
Expand Down
80 changes: 80 additions & 0 deletions scripts/checks/build-protected-managed-images.sh
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,65 @@ for command in curl docker jq node sha256sum; do
}
done

audit_evidence_dir=""
audit_receipt=""
audit_raw_report=""
audit_receipt_sha256=""
if [[ -n "$cache_to" ]]; then
audit_evidence_dir="$cache_to/reviewed-npm-audit"
docker buildx build \
--file "$source_root/Dockerfile.protected-npm-audit" \
--platform "$platform" \
--target protected-mcporter-audit-evidence \
--output "type=local,dest=${audit_evidence_dir}" \
--provenance=false \
--sbom=false \
"$source_root"
elif [[ -n "$cache_from" ]]; then
audit_evidence_dir="$cache_from/reviewed-npm-audit"
fi

if [[ -n "$audit_evidence_dir" ]]; then
[[ -d "$audit_evidence_dir" && ! -L "$audit_evidence_dir" ]] || {
echo "ERROR: protected managed-image cache has no reviewed mcporter audit evidence" >&2
exit 1
}
[[ -z "$(find "$audit_evidence_dir" -type l -print -quit)" ]] || {
echo "ERROR: protected managed-image reviewed audit evidence contains a symlink" >&2
exit 1
}
audit_receipt="$audit_evidence_dir/mcporter-runtime.receipt.json"
audit_raw_report="$audit_evidence_dir/mcporter-runtime.raw.json"
audit_receipt_sha_file="$audit_evidence_dir/mcporter-runtime.receipt.sha256"
[[ -f "$audit_receipt" && ! -L "$audit_receipt" && -s "$audit_receipt" ]] || {
echo "ERROR: protected managed-image reviewed audit receipt is missing or unsafe" >&2
exit 1
}
[[ -f "$audit_raw_report" && ! -L "$audit_raw_report" && -s "$audit_raw_report" ]] || {
echo "ERROR: protected managed-image reviewed audit raw report is missing or unsafe" >&2
exit 1
}
audit_receipt_sha256="$(sha256sum "$audit_receipt" | awk '{print $1}')"
[[ "$audit_receipt_sha256" =~ ^[a-f0-9]{64}$ ]] || {
echo "ERROR: protected managed-image reviewed audit receipt hash is invalid" >&2
exit 1
}
if [[ -n "$cache_to" ]]; then
printf '%s\n' "$audit_receipt_sha256" >"$audit_receipt_sha_file"
chmod 0400 "$audit_receipt_sha_file"
else
[[ -f "$audit_receipt_sha_file" && ! -L "$audit_receipt_sha_file" ]] || {
echo "ERROR: protected managed-image reviewed audit receipt hash is missing or unsafe" >&2
exit 1
}
read -r recorded_audit_receipt_sha256 <"$audit_receipt_sha_file"
[[ "$recorded_audit_receipt_sha256" =~ ^[a-f0-9]{64}$ && "$recorded_audit_receipt_sha256" == "$audit_receipt_sha256" ]] || {
echo "ERROR: protected managed-image reviewed audit receipt hash does not match" >&2
exit 1
}
fi
fi

work_dir="$(mktemp -d "${RUNNER_TEMP:-/tmp}/nemoclaw-protected-images.XXXXXX")"
seed_overlay_active=0
seed_backup="$work_dir/npm-cache-seed-original"
Expand All @@ -193,6 +252,17 @@ trap restore_worktree EXIT
trap 'exit 130' INT
trap 'exit 143' TERM

if [[ -n "$cache_to" ]]; then
cp -pR -- "$source_seed_dir" "$seed_backup"
seed_overlay_active=1
install -d -m 0700 "$source_seed_dir/reviewed-npm-audit"
install -m 0400 \
"$audit_receipt" \
"$audit_raw_report" \
"$audit_receipt_sha_file" \
"$source_seed_dir/reviewed-npm-audit/"
fi

if [[ -n "$cache_from" ]]; then
imported_seed="$work_dir/npm-cache-seed-import"
node --experimental-strip-types --no-warnings "$seed_helper" copy \
Expand Down Expand Up @@ -363,6 +433,13 @@ build_agent() {
cache_args+=(--cache-from "type=local,src=${cache_source}")
fi
fi
if [[ "$agent" == "openclaw" && -n "$audit_receipt" ]]; then
cache_args+=(
--secret "id=nemoclaw-mcporter-audit-receipt,src=${audit_receipt}"
--secret "id=nemoclaw-mcporter-audit-raw-report,src=${audit_raw_report}"
--build-arg "NEMOCLAW_MCPORTER_AUDIT_RECEIPT_SHA256=${audit_receipt_sha256}"
)
fi

local base_digest="${base_reference##*@}"
docker buildx imagetools inspect "$base_reference" --raw >"$exact_base_raw"
Expand Down Expand Up @@ -489,6 +566,9 @@ if [[ -n "$cache_to" ]]; then
--os "$npm_target_os" \
--cpu "$npm_target_cpu" \
--libc "$npm_target_libc"
cp -pR -- \
"$source_seed_dir/reviewed-npm-audit" \
"$cache_to/npm-cache-seed/reviewed-npm-audit"
node --experimental-strip-types --no-warnings "$seed_helper" export \
--lockfile "$source_mcp_lockfile" \
--output "$cache_to/mcp-runtime-npm-cache-seed" \
Expand Down
Loading
Loading