Skip to content

Security(M-05): Nebi workspace selection is shell-interpolated and init failures are masked #173

Description

@dcmcand

Summary

A user-selected Nebi workspace value is inserted into a shell command that runs nebi pull and pixi install. The value is interpolated into an sh -c string rather than passed as an argument array, and any failure in the chain is downgraded to a warning so the pod continues after a failed or partial environment operation. The init container also holds a short-lived Nebi credential and has no explicit security context in this custom code.

Severity: Medium · CWE-78 (OS Command Injection)
Validation: Confirmed against HEAD f932d80 on 2026-07-14 (assessed at 69c84f7; unchanged since).

Evidence

  • User-controlled value: config/jupyterhub/01-spawner.py:692-694 reads conda_env from spawner.user_options and derives workspace_name via rsplit, with no validation anywhere in config/jupyterhub/.
  • Shell interpolation + masked failure: 01-spawner.py:751-761 builds /bin/sh -c "mkdir -p {ws_dir} && nebi pull {workspace_name} -o {ws_dir} --force && pixi install ... || echo 'WARNING: ...'". The trailing || echo catches any failure in the && chain and exits 0, so the init container succeeds even when the environment operation failed.
  • Credential in init env: 01-spawner.py:719-722 sets NEBI_AUTH_TOKEN to the exchanged JWT.
  • No explicit security context: the nebi-pull init container dict (01-spawner.py:747-764) has no securityContext; the pod-level context (01-spawner.py:133) sets only fsGroup. This container runs as the pod default (non-root), so injected code runs at the user's own identity, not root.

Impact

The user already has code-execution authority in their own environment, so this is not an independent tenant escape. It does broaden command interpretation before the intended environment starts, places a credential in a shell-driven path, hides integrity failures, and undermines reproducibility. A user_options value like foo;<cmd> (no slash) survives rsplit intact into the shell string.

Remediation

  • Validate workspace IDs against a strict grammar.
  • Pass arguments without a shell.
  • Give the init container an explicit restricted security context.
  • Use a single-purpose, workspace-bound token.
  • Fail the spawn when pull or install fails, rather than logging a warning and continuing.

Acceptance criteria

  • Shell metacharacters cannot change execution.
  • Failed initialization prevents readiness.
  • The token cannot access another user's workspace.

Source: data-science-pack 0.1.0 security assessment (pinned commit 69c84f72df259ec755ed40bfc83f20158c550d55), finding M-05.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: securitySecurity findings and hardening workpriority: medium ⚡Medium priority - standard queue

    Type

    No type

    Fields

    Priority

    None yet

    Start date

    None yet

    Target date

    None yet

    Size

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions