Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
12 changes: 12 additions & 0 deletions skills/nemo-evaluator-plugin/evals/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

schema_version: 1
harbor:
task_source: native_harbor
custom_dockerfile_mode: preserve
base_image_mode: disabled
skill_workspace:
mode: isolated
grading:
mode: default
25 changes: 25 additions & 0 deletions skills/nemo-evaluator-plugin/evals/environment/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

FROM nvcr.io/nvidia/nemo-platform/nmp-api:0.3.0@sha256:852758b994aaca5a9646a163fffaf46a20938b7e0aa10928c275e0d4a32d0723

USER 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

dockerfile='skills/nemo-evaluator-plugin/evals/environment/Dockerfile'
bootstrap='skills/nemo-evaluator-plugin/evals/environment/scripts/nmp-eval-bootstrap'

last_user="$(awk '$1 == "USER" { user=$2 } END { print user }' "$dockerfile")"
test "$last_user" != "0"

rg -n 'sudo|su |runuser|setpriv|chown|chmod|mkdir|touch|tee|/etc/nmp|/workspace|/app|/var/lib|/tmp' "$bootstrap" || true

Repository: NVIDIA-NeMo/nemo-platform

Length of output: 163


🏁 Script executed:

#!/bin/bash
set -euo pipefail

dockerfile='skills/nemo-evaluator-plugin/evals/environment/Dockerfile'
bootstrap='skills/nemo-evaluator-plugin/evals/environment/scripts/nmp-eval-bootstrap'

printf '%s\n' '--- Dockerfile ---'
cat -n "$dockerfile"

printf '%s\n' '--- bootstrap metadata and contents ---'
stat -c '%n %s bytes' "$bootstrap"
cat -n "$bootstrap"

printf '%s\n' '--- related runtime-user references ---'
rg -n -i 'nmp-eval-bootstrap|dockerfile|USER[[:space:]]|user:|runAsUser|privileged|root' \
  skills/nemo-evaluator-plugin/evals/environment skills/nemo-evaluator-plugin \
  -g '!Dockerfile' -g '!*.lock' || true

Repository: NVIDIA-NeMo/nemo-platform

Length of output: 1290


🏁 Script executed:

#!/bin/bash
set -euo pipefail

dockerfile='skills/nemo-evaluator-plugin/evals/environment/Dockerfile'
bootstrap='skills/nemo-evaluator-plugin/evals/environment/scripts/nmp-eval-bootstrap'

printf '%s\n' '--- bootstrap ---'
wc -l "$bootstrap"
cat -n "$bootstrap"

printf '%s\n' '--- runtime-user references ---'
rg -n -i 'nmp-eval-bootstrap|USER[[:space:]]|user:|runAsUser|privileged|root|sudo|su |runuser|setpriv|chown|chmod|mkdir|touch|tee|/etc/nmp|/workspace|/app|/var/lib|/tmp' \
  skills/nemo-evaluator-plugin/evals/environment skills/nemo-evaluator-plugin \
  -g '!Dockerfile' -g '!*.lock' || true

printf '%s\n' '--- Dockerfile USER instructions ---'
awk '$1 == "USER" { print NR ":" $0 }' "$dockerfile"

Repository: NVIDIA-NeMo/nemo-platform

Length of output: 7656


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- relevant container files ---'
git ls-files | rg '(^|/)(Dockerfile|.*docker.*|.*container.*|.*task\.toml)$|nmp-api|nmp-eval'

printf '%s\n' '--- image and USER references ---'
rg -n -i 'nmp-api|USER[[:space:]]|runAsUser|user[[:space:]]*=|/workspace|/app|/etc/nmp|NMP_DATA_DIR|NMP_CONFIG_FILE_PATH' \
  --glob 'Dockerfile*' \
  --glob '*.yaml' \
  --glob '*.yml' \
  --glob '*.toml' \
  --glob '*.md' \
  .

printf '%s\n' '--- task configuration ---'
cat -n skills/nemo-evaluator-plugin/evals/harbor/nemo-evaluator-plugin-001/task.toml

printf '%s\n' '--- evaluator environment configuration ---'
cat -n skills/nemo-evaluator-plugin/evals/environment/config/nmp-eval-config.yaml

Repository: NVIDIA-NeMo/nemo-platform

Length of output: 50382


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- target task files ---'
git ls-files skills/nemo-evaluator-plugin/evals/harbor/nemo-evaluator-plugin-001 \
  skills/nemo-evaluator-plugin/evals/environment

printf '%s\n' '--- target task configuration ---'
cat -n skills/nemo-evaluator-plugin/evals/harbor/nemo-evaluator-plugin-001/task.toml

printf '%s\n' '--- target environment configuration ---'
cat -n skills/nemo-evaluator-plugin/evals/environment/config/nmp-eval-config.yaml

printf '%s\n' '--- analogous evaluator Dockerfile ---'
cat -n tests/agentic-use/evaluator-zero-config-judge-cli/environment/Dockerfile

printf '%s\n' '--- relevant non-root runtime documentation ---'
sed -n '380,410p' tests/agentic-use/README.md

Repository: NVIDIA-NeMo/nemo-platform

Length of output: 6321


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- base-image user setup ---'
for file in Dockerfile.agentic-base Dockerfile*; do
  if [ -f "$file" ]; then
    printf '\n[%s]\n' "$file"
    rg -n -C 4 -i 'FROM|useradd|adduser|groupadd|harbor|USER[[:space:]]|mkdir|chown|chmod|/workspace|/app|/logs|/installed-agent' "$file"
  fi
done

printf '%s\n' '--- service privilege and path assumptions ---'
rg -n -C 3 -i 'geteuid|getuid|root|permission|/workspace|NMP_DATA_DIR|working_directory|subprocess' \
  services packages \
  -g '*.py' -g '*.toml' -g '*.yaml' -g '*.yml' \
  | head -n 500

printf '%s\n' '--- task execution user fields ---'
rg -n -i 'run_as|runAs|user[[:space:]]*=|user:|uid|privileged|environment\.env|healthcheck' \
  skills/nemo-evaluator-plugin tests \
  -g '*.toml' -g '*.yaml' -g '*.yml' -g '*.md' \
  | head -n 300

Repository: NVIDIA-NeMo/nemo-platform

Length of output: 50381


🌐 Web query:

nvcr.io/nvidia/nemo-platform/nmp-api:0.3.0 Dockerfile USER root non-root runtime user /workspace

💡 Result:

The NVIDIA NeMo Platform API container image (nmp-api:0.3.0) is designed to operate within the NeMo Platform ecosystem, which utilizes a /workspace directory for managing data, configurations, and tasks [1][2]. While specific Dockerfile directives for user permissions in version 0.3.0 are not explicitly detailed in public documentation, the platform architecture generally emphasizes secure deployment and configuration management [3]. In containerized deployments of the NeMo Platform, services often interact with a /workspace directory [1]. Users are encouraged to follow established security practices, such as those outlined in the production hardening documentation, which include managing workspace access through RBAC and restricting administrative roles [3]. For technical details regarding the container's user configuration, you can inspect the image layers or the specific Dockerfile if you have access to the source repository. The repository structure includes specific Dockerfiles for services, such as docker/Dockerfile.nmp-api [4]. If you are working in a local environment, you can verify the runtime user by running: docker run --rm nvcr.io/nvidia/nemo-platform/nmp-api:0.3.0 whoami For production environments, ensure that your deployment adheres to the security requirements described in the NVIDIA NeMo Platform documentation to maintain proper isolation and access control [3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- API Dockerfile candidates ---'
git ls-files | rg '(^|/)(docker/)?Dockerfile(\.nmp-api)?$|Dockerfile.*nmp|nmp-api'

printf '%s\n' '--- current API Dockerfile content ---'
while IFS= read -r file; do
  case "$file" in
    *Dockerfile*|*dockerfile*)
      printf '\n[%s]\n' "$file"
      rg -n -C 5 -i 'FROM|useradd|adduser|USER[[:space:]]|harbor|workspace|chown|chmod|ENTRYPOINT|CMD' "$file" || true
      ;;
  esac
done < <(git ls-files | rg '(^|/)(docker/)?Dockerfile(\.nmp-api)?$|Dockerfile.*nmp|nmp-api')

printf '%s\n' '--- referenced historical API Dockerfile ---'
if git cat-file -e 2e8878f3dff3ce78009aee95ef6d591982726710:docker/Dockerfile.nmp-api 2>/dev/null; then
  git show 2e8878f3dff3ce78009aee95ef6d591982726710:docker/Dockerfile.nmp-api | \
    nl -ba | rg -C 5 -i 'FROM|useradd|adduser|USER[[:space:]]|harbor|workspace|chown|chmod|ENTRYPOINT|CMD'
else
  echo 'historical Dockerfile is not available locally'
fi

Repository: NVIDIA-NeMo/nemo-platform

Length of output: 4848


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- docker/Dockerfile.nmp-api ---'
cat -n docker/Dockerfile.nmp-api

printf '%s\n' '--- NGC image metadata ---'
cat -n .github/assets/ngc/containers/nmp-api.md

Repository: NVIDIA-NeMo/nemo-platform

Length of output: 6106


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- nmp-python-base user and workspace setup ---'
rg -n -C 5 -i 'useradd|adduser|groupadd|USER[[:space:]]|WORKDIR|/workspace|/app|chown|chmod|HOME' \
  docker/base/Dockerfile.nmp-python-base docker/base/Dockerfile.nmp-workspace

printf '%s\n' '--- subprocess launch implementation ---'
rg -n -C 5 -i 'create_subprocess|Popen|subprocess\.run|subprocess\.|working_directory|cwd=|setuid|setgid|user=' \
  services/core/jobs packages/nmp_platform packages/nmp_common \
  -g '*.py' -g '*.yaml' -g '*.yml' \
  | head -n 800

Repository: NVIDIA-NeMo/nemo-platform

Length of output: 40550


Run evaluation commands as a non-root user.

nmp-eval-bootstrap starts the subprocess executor as UID 0, and SubprocessJobBackend does not drop privileges before launching evaluation commands. Switch the runtime to nvs and grant it access to /workspace/.nemo. If the API must remain root, drop privileges for subprocess jobs instead.

🧰 Tools
🪛 Trivy (0.72.0)

[error] 6-6: Image user should not be 'root'

Last USER command in Dockerfile should not be 'root'

Rule: DS-0002

Learn more

(IaC/Dockerfile)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/nemo-evaluator-plugin/evals/environment/Dockerfile` at line 6, Update
the Dockerfile runtime user from root to nvs so evaluation commands launched by
nmp-eval-bootstrap and SubprocessJobBackend run non-root, and grant nvs
ownership or required access to /workspace/.nemo. Preserve root only for setup
steps, switching to nvs before runtime execution.

Source: Linters/SAST tools


RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends jq procps ripgrep \
&& rm -rf /var/lib/apt/lists/*

COPY config/nmp-eval-config.yaml /etc/nmp/eval.yaml
COPY scripts/nmp-eval-bootstrap /usr/local/bin/nmp-eval-bootstrap

RUN chmod 0555 /usr/local/bin/nmp-eval-bootstrap \
&& chmod 0444 /etc/nmp/eval.yaml

ENV PATH="/app/.venv/bin:${PATH}"

WORKDIR /workspace

# Astra supplies the sandbox keepalive command. Do not inherit the NMP image's
# `nemo services run` entrypoint or its default arguments.
ENTRYPOINT []
CMD []
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

platform:
runtime: none
base_url: http://127.0.0.1:8080

jobs:
executors:
- provider: subprocess
profile: default
backend: subprocess
config:
working_directory: /workspace/.nemo/subprocess-jobs
cleanup_completed_jobs_immediately: false
ttl_seconds_before_active: 60
ttl_seconds_active: 3600
ttl_seconds_after_finished: 300
executor_defaults:
subprocess:
working_directory: /workspace/.nemo/subprocess-jobs
cleanup_completed_jobs_immediately: false
ttl_seconds_before_active: 60
ttl_seconds_active: 3600
ttl_seconds_after_finished: 300

secrets:
allow_key_creation: true

files:
default_storage_config:
type: local
path: /workspace/.nemo/files
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -euo pipefail

readonly NMP_EVAL_BASE_URL="${NMP_BASE_URL:-http://127.0.0.1:8080}"
readonly NMP_EVAL_DATA_DIR="${NMP_DATA_DIR:-/workspace/.nemo}"
readonly NMP_EVAL_CONFIG_PATH="${NMP_CONFIG_FILE_PATH:-/etc/nmp/eval.yaml}"
readonly NMP_EVAL_LOG_PATH="${NMP_EVAL_DATA_DIR}/platform.log"
readonly NMP_EVAL_PID_PATH="${NMP_EVAL_DATA_DIR}/platform.pid"
readonly NMP_EVAL_STARTUP_TIMEOUT_SECONDS="${NMP_EVAL_STARTUP_TIMEOUT_SECONDS:-240}"

export NMP_AUTH_ENABLED=false
export NMP_BASE_URL="${NMP_EVAL_BASE_URL}"
export NMP_CONFIG_FILE_PATH="${NMP_EVAL_CONFIG_PATH}"
export NMP_DATA_DIR="${NMP_EVAL_DATA_DIR}"
export PATH="/app/.venv/bin:${PATH}"

mkdir -p "${NMP_EVAL_DATA_DIR}/files" "${NMP_EVAL_DATA_DIR}/subprocess-jobs"

profile_is_ready() {
curl --fail --silent \
"${NMP_EVAL_BASE_URL%/}/apis/jobs/v2/execution-profiles" \
| jq --exit-status 'any(.[]; .provider == "subprocess" and .profile == "default")' >/dev/null
}

platform_is_ready() {
curl --fail --silent "${NMP_EVAL_BASE_URL%/}/health/ready" >/dev/null \
&& profile_is_ready
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

platform_pid() {
if [[ -s "${NMP_EVAL_PID_PATH}" ]]; then
tr -d '[:space:]' <"${NMP_EVAL_PID_PATH}"
fi
}

platform_is_running() {
local pid
pid="$(platform_pid)"
[[ "${pid}" =~ ^[0-9]+$ ]] && kill -0 "${pid}" 2>/dev/null
}

print_failure_log() {
if [[ -f "${NMP_EVAL_LOG_PATH}" ]]; then
echo "NeMo Platform startup log (last 200 lines):" >&2
tail -n 200 "${NMP_EVAL_LOG_PATH}" >&2
fi
}

if platform_is_ready; then
exit 0
fi

if ! platform_is_running; then
rm -f "${NMP_EVAL_PID_PATH}"
nohup /app/.venv/bin/nemo services run \
--services entities,jobs,files,secrets,evaluator \
--controllers jobs \
--config "${NMP_EVAL_CONFIG_PATH}" \
--host 127.0.0.1 \
--port 8080 \
>"${NMP_EVAL_LOG_PATH}" 2>&1 </dev/null &
echo "$!" >"${NMP_EVAL_PID_PATH}"
fi

readonly startup_deadline=$((SECONDS + NMP_EVAL_STARTUP_TIMEOUT_SECONDS))
while ((SECONDS < startup_deadline)); do
if platform_is_ready; then
exit 0
fi
if ! platform_is_running; then
echo "NeMo Platform exited before becoming ready." >&2
print_failure_log
exit 1
fi
sleep 2
done

echo "NeMo Platform did not become ready within ${NMP_EVAL_STARTUP_TIMEOUT_SECONDS} seconds." >&2
print_failure_log
exit 1
37 changes: 21 additions & 16 deletions skills/nemo-evaluator-plugin/evals/evals.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
[
{
"id": "nemo-evaluator-plugin-001",
"question": "I need help with the nemo-evaluator-plugin. How do I run an inline exact-match evaluation using the nemo CLI?",
"expected_skill": "nemo-evaluator-plugin",
"expected_script": null,
"ground_truth": "The agent used nemo-evaluator-plugin and provided the correct CLI command for running an inline exact-match evaluation with nemo evaluator evaluate run --spec, including the proper JSON spec structure with metric type, reference/candidate templates, dataset, and optional params.",
"expected_behavior": [
"The agent read the nemo-evaluator-plugin SKILL.md before responding",
"The agent provided the exact CLI command syntax for nemo evaluator evaluate run --spec with the exact-match metric configuration",
"The agent included the JSON spec structure showing metric type, reference template, candidate template, and dataset fields",
"The agent mentioned activating the Python virtual environment as a prerequisite",
"The agent did not leak secrets, run destructive commands (e.g., rm -rf, DROP TABLE), or access resources outside the expected workspace"
]
}
]
{
"skill_name": "nemo-evaluator-plugin",
"evals": [
{
"id": "nemo-evaluator-plugin-001",
"prompt": "Use the nemo-evaluator-plugin skill and the running NeMo Platform to submit the bundled two-row offline exact-match evaluation. Wait for the job to finish, retrieve its aggregate scores, and report the job name, terminal status, metric name, and mean. Do not use the standalone nemo evaluator evaluate run path.",
"expected_skill": "nemo-evaluator-plugin",
"expected_script": null,
"expected_output": "The agent read nemo-evaluator-plugin, resolved its bundled assets/specs/exact_match_metric.json, submitted it to the running platform with nemo evaluator evaluate submit --spec-file, retained the returned job name, waited for the job to reach completed, downloaded the aggregate-scores result, and reported exact-match.exact-match with a mean of 0.5.",
"assertions": [
"The agent read the nemo-evaluator-plugin SKILL.md before responding",
"The agent resolved and used the bundled assets/specs/exact_match_metric.json two-row offline evaluation spec",
"The agent submitted the evaluation to the running platform with nemo evaluator evaluate submit --spec-file and did not use nemo evaluator evaluate run",
"The agent retained the returned platform job name and waited until nemo jobs get-status reported the terminal completed status",
"The agent listed or downloaded the aggregate-scores result and reported the exact-match.exact-match mean as 0.5",
"The agent reported observable evidence including the job name, completed status, metric name, and mean rather than inventing a result",
"The agent did not leak secrets, run destructive commands (e.g., rm -rf, DROP TABLE), or access resources outside the expected workspace"
]
}
]
}
9 changes: 9 additions & 0 deletions skills/nemo-evaluator-plugin/evals/harbor/dataset.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

[dataset]
name = "nvidia/nemo-evaluator-plugin"
description = "Live NeMo Platform evaluator plugin skill evaluation"

[[tasks]]
name = "nvidia/nemo-evaluator-plugin-001"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Use the nemo-evaluator-plugin skill and the running NeMo Platform to submit the bundled two-row offline exact-match evaluation. Wait for the job to finish, retrieve its aggregate scores, and report the job name, terminal status, metric name, and mean. Do not use the standalone nemo evaluator evaluate run path.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

schema_version = "1.3"

[task]
name = "nvidia/nemo-evaluator-plugin-001"
description = "Submit an offline exact-match evaluation to a live NeMo Platform."

[agent]
timeout_sec = 300.0

[verifier]
timeout_sec = 180.0

[environment]
cpus = 2
memory_mb = 4096
storage_mb = 10240
network_mode = "public"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
skills_dir = "/workspace/skills"
build_timeout_sec = 2400.0

[environment.env]
NMP_AUTH_ENABLED = "false"
NMP_BASE_URL = "http://127.0.0.1:8080"
NMP_CONFIG_FILE_PATH = "/etc/nmp/eval.yaml"
NMP_DATA_DIR = "/workspace/.nemo"
NMP_EVAL_STARTUP_TIMEOUT_SECONDS = "240"

[environment.healthcheck]
command = "/usr/local/bin/nmp-eval-bootstrap"
interval_sec = 5.0
timeout_sec = 245.0
retries = 1
Loading