diff --git a/cgmanifest.json b/cgmanifest.json index 3f10f43dbc..efa809951c 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -1510,16 +1510,6 @@ } } }, - { - "Component": { - "Type": "git", - "Git": { - "Name": "nvs", - "repositoryUrl": "https://github.com/jasongin/nvs", - "commitHash": "6b20e9f750ea371bd347e5dbac8406d677292b60" - } - } - }, { "Component": { "Type": "other", diff --git a/src/universal/.devcontainer/devcontainer.json b/src/universal/.devcontainer/devcontainer.json index 2ebf06740f..c8b45688d2 100644 --- a/src/universal/.devcontainer/devcontainer.json +++ b/src/universal/.devcontainer/devcontainer.json @@ -21,7 +21,6 @@ "version": "24", "additionalVersions": "22" }, - "./local-features/nvs": "latest", "ghcr.io/devcontainers/features/python:1": { "version": "3.14.2", "additionalVersions": "3.13.8", @@ -84,7 +83,6 @@ "ghcr.io/devcontainers/features/dotnet", "ghcr.io/devcontainers/features/hugo", "ghcr.io/devcontainers/features/node", - "./local-features/nvs", "ghcr.io/devcontainers/features/conda", "./local-features/patch-conda", "ghcr.io/devcontainers/features/python", diff --git a/src/universal/.devcontainer/local-features/nvs/devcontainer-feature.json b/src/universal/.devcontainer/local-features/nvs/devcontainer-feature.json deleted file mode 100644 index 610e683ad2..0000000000 --- a/src/universal/.devcontainer/local-features/nvs/devcontainer-feature.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "id": "nvs", - "name": "Node Version Switcher", - "containerEnv": { - "NVS_DIR": "/usr/local/nvs", - "PATH": "${NVS_DIR}:${PATH}" - }, - "install": { - "app": "", - "file": "install.sh" - } -} \ No newline at end of file diff --git a/src/universal/.devcontainer/local-features/nvs/install.sh b/src/universal/.devcontainer/local-features/nvs/install.sh deleted file mode 100644 index fb12899a78..0000000000 --- a/src/universal/.devcontainer/local-features/nvs/install.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env bash -#------------------------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. -#------------------------------------------------------------------------------------------------------------- - -USERNAME=${USERNAME:-"codespace"} -NVS_HOME=${NVS_HOME:-"/usr/local/nvs"} - -set -eux - -if [ "$(id -u)" -ne 0 ]; then - echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' - exit 1 -fi - -# Ensure that login shells get the correct path if the user updated the PATH using ENV. -rm -f /etc/profile.d/00-restore-env.sh -echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh -chmod +x /etc/profile.d/00-restore-env.sh - -# Function to run apt-get if needed -apt_get_update_if_needed() -{ - if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then - echo "Running apt-get update..." - apt-get update - else - echo "Skipping apt-get update." - fi -} - -# Checks if packages are installed and installs them if not -check_packages() { - if ! dpkg -s "$@" > /dev/null 2>&1; then - apt_get_update_if_needed - apt-get -y install --no-install-recommends "$@" - fi -} - -updaterc() { - echo "Updating /etc/bash.bashrc and /etc/zsh/zshrc..." - if [[ "$(cat /etc/bash.bashrc)" != *"$1"* ]]; then - echo -e "$1" >> /etc/bash.bashrc - fi - if [ -f "/etc/zsh/zshrc" ] && [[ "$(cat /etc/zsh/zshrc)" != *"$1"* ]]; then - echo -e "$1" >> /etc/zsh/zshrc - fi -} - -export DEBIAN_FRONTEND=noninteractive - -if ! cat /etc/group | grep -e "^nvs:" > /dev/null 2>&1; then - groupadd -r nvs -fi -usermod -a -G nvs "${USERNAME}" - -git config --global --add safe.directory ${NVS_HOME} -mkdir -p ${NVS_HOME} - -git clone -c advice.detachedHead=false --depth 1 https://github.com/jasongin/nvs ${NVS_HOME} 2>&1 -(cd ${NVS_HOME} && git remote get-url origin && echo $(git log -n 1 --pretty=format:%H -- .)) > ${NVS_HOME}/.git-remote-and-commit -bash ${NVS_HOME}/nvs.sh install -rm ${NVS_HOME}/cache/* - -# Clean up -rm -rf ${NVS_HOME}/.git - -updaterc "if [[ \"\${PATH}\" != *\"${NVS_HOME}\"* ]]; then export PATH=${NVS_HOME}:\${PATH}; fi" - -chown -R "${USERNAME}:nvs" "${NVS_HOME}" -chmod -R g+r+w "${NVS_HOME}" -find "${NVS_HOME}" -type d | xargs -n 1 chmod g+s - -NVS="/home/codespace/.nvs" -mkdir -p ${NVS} -ln -snf ${NVS_HOME}/* $NVS - -echo "Done!" diff --git a/src/universal/.devcontainer/local-features/patch-conda/install.sh b/src/universal/.devcontainer/local-features/patch-conda/install.sh index 11d0a69bc3..10ddd0a63d 100644 --- a/src/universal/.devcontainer/local-features/patch-conda/install.sh +++ b/src/universal/.devcontainer/local-features/patch-conda/install.sh @@ -48,6 +48,20 @@ update_conda_package() { sudo_if /opt/conda/bin/python3 -m pip install --upgrade pip +# Remove any cached pip packages under /opt/conda/pkgs vulnerable to GHSA-jp4c-xjxw-mgf9 +# (pip < 26.1). Only pip cache entries older than 26.1 are removed; this does not touch +# the active pip install in site-packages (upgraded above via pip). +for pkg_path in /opt/conda/pkgs/pip-[0-9]*; do + [ -e "$pkg_path" ] || continue + pkg_name="$(basename "$pkg_path")" + pkg_version="${pkg_name#pip-}" + pkg_version="${pkg_version%%-*}" + greater_version="$(printf '%s\n%s\n' "$pkg_version" "26.1" | sort -V | tail -1)" + if [ "$pkg_version" != "$greater_version" ]; then + sudo_if "rm -rf $pkg_path" + fi +done + # Temporary: Upgrade python packages due to security vulnerabilities # They are installed by the conda feature and Conda distribution does not have the patches diff --git a/src/universal/.devcontainer/local-features/patch-python/install.sh b/src/universal/.devcontainer/local-features/patch-python/install.sh index ff9604ca91..e9aa4b7b00 100644 --- a/src/universal/.devcontainer/local-features/patch-python/install.sh +++ b/src/universal/.devcontainer/local-features/patch-python/install.sh @@ -5,6 +5,7 @@ #------------------------------------------------------------------------------------------------------------- USERNAME=${USERNAME:-"codespace"} +PATCH_PYTHON_PIP_UPGRADE_PATH=${PATCH_PYTHON_PIP_UPGRADE_PATH:-"/usr/local/python/3.13.8/bin/python"} set -eux @@ -38,3 +39,9 @@ update_package() { sudo_if "$PYTHON_PATH -m pip install --upgrade --no-cache-dir $PACKAGE==$VERSION" sudo_if "$PYTHON_PATH -m pip show --no-python-version-warning $PACKAGE" } + +# Updating pip version for python 3.13.8. Must be removed when pinned version 3.13.8 is updated to a different python version. +# https://github.com/advisories/GHSA-jp4c-xjxw-mgf9 +if [ -x "$PATCH_PYTHON_PIP_UPGRADE_PATH" ]; then + sudo_if "$PATCH_PYTHON_PIP_UPGRADE_PATH -m pip install --upgrade pip" +fi diff --git a/src/universal/.devcontainer/local-features/setup-user/devcontainer-feature.json b/src/universal/.devcontainer/local-features/setup-user/devcontainer-feature.json index 60f864e4df..53a9ef2f40 100644 --- a/src/universal/.devcontainer/local-features/setup-user/devcontainer-feature.json +++ b/src/universal/.devcontainer/local-features/setup-user/devcontainer-feature.json @@ -15,7 +15,6 @@ "ORYX_ENV_TYPE": "vsonline-present", "PYTHONIOENCODING": "UTF-8", "NPM_GLOBAL": "/home/codespace/.npm-global", - "NVS_HOME": "/home/codespace/.nvs", "RVM_PATH": "/usr/local/rvm", "RAILS_DEVELOPMENT_HOSTS": ".githubpreview.dev,.preview.app.github.dev,.app.github.dev", "GOROOT": "/usr/local/go", diff --git a/src/universal/README.md b/src/universal/README.md index 0eb3545499..f4f916da7c 100644 --- a/src/universal/README.md +++ b/src/universal/README.md @@ -28,8 +28,8 @@ You can decide how often you want updates by referencing a [semantic version](ht For example: - `mcr.microsoft.com/devcontainers/universal:6-noble` -- `mcr.microsoft.com/devcontainers/universal:6.0-noble` -- `mcr.microsoft.com/devcontainers/universal:6.0.8-noble` +- `mcr.microsoft.com/devcontainers/universal:6.1-noble` +- `mcr.microsoft.com/devcontainers/universal:6.1.0-noble` See [history](history) for information on the contents of each version and [here for a complete list of available tags](https://mcr.microsoft.com/v2/devcontainers/universal/tags/list). @@ -47,7 +47,7 @@ Access to the Anaconda repository is covered by the [Anaconda Terms of Service]( While the image itself works unmodified, you can also directly reference pre-built versions of `Dockerfile` by using the `image` property in `.devcontainer/devcontainer.json` or updating the `FROM` statement in your own `Dockerfile` to: -`mcr.microsoft.com/devcontainers/universal:5.1-linux` +`mcr.microsoft.com/devcontainers/universal:6.1-linux` Alternatively, you can use the contents of [.devcontainer](.devcontainer) to fully customize your container's contents or to build it for a container host architecture not supported by the image. diff --git a/src/universal/manifest.json b/src/universal/manifest.json index ce34d88cb2..8311842f6b 100644 --- a/src/universal/manifest.json +++ b/src/universal/manifest.json @@ -1,5 +1,5 @@ { - "version": "6.0.8", + "version": "6.1.0", "build": { "latest": true, "rootDistro": "debian", @@ -87,7 +87,6 @@ "git": { "Oh My Zsh!": "/home/codespace/.oh-my-zsh", "nvm": "/usr/local/share/nvm", - "nvs": "/usr/local/nvs", "rbenv": "/usr/local/share/rbenv", "ruby-build": "/usr/local/share/ruby-build" }, diff --git a/src/universal/test-project/test-utils.sh b/src/universal/test-project/test-utils.sh index 872ec8ee66..575a9e6211 100644 --- a/src/universal/test-project/test-utils.sh +++ b/src/universal/test-project/test-utils.sh @@ -229,6 +229,21 @@ checkPythonPackageVersion() check-version-ge "${PACKAGE}-requirement" "${current_version}" "${REQUIRED_VERSION}" } +checkPythonPipVersion() +{ + PYTHON_PATH=$1 + REQUIRED_VERSION=$2 + + if [ ! -x "${PYTHON_PATH}" ]; then + echo "failing checkPythonPipVersion because python path is not executable: ${PYTHON_PATH}" + echoStderr "❌ Python not found at ${PYTHON_PATH}" + return 1 + fi + + current_version=$("${PYTHON_PATH}" -m pip --version | awk '{print $2}') + check-version-ge "pip-requirement" "${current_version}" "${REQUIRED_VERSION}" +} + checkCondaPackageVersion() { PACKAGE=$1 @@ -237,6 +252,25 @@ checkCondaPackageVersion() check-version-ge "conda-${PACKAGE}-requirement" "${current_version}" "${REQUIRED_VERSION}" } +checkNoVulnerablePipCache() +{ + CACHE_DIR=$1 + MIN_VERSION=$2 + + for pkg_path in "${CACHE_DIR}"/pip-[0-9]*; do + [ -e "${pkg_path}" ] || continue + pkg_name="$(basename "${pkg_path}")" + pkg_version="${pkg_name#pip-}" + pkg_version="${pkg_version%%-*}" + greater_version="$(printf '%s\n%s\n' "${pkg_version}" "${MIN_VERSION}" | sort -V | tail -1)" + if [ "${pkg_version}" != "${greater_version}" ]; then + echoStderr "Found vulnerable cached pip package: ${pkg_path} (version ${pkg_version} < ${MIN_VERSION})" + return 1 + fi + done + return 0 +} + checkBundledNpmVersion() { NODE_VERSION=$1 diff --git a/src/universal/test-project/test.sh b/src/universal/test-project/test.sh index 8efb073e54..8589a7a0e5 100755 --- a/src/universal/test-project/test.sh +++ b/src/universal/test-project/test.sh @@ -82,7 +82,6 @@ checkDirectoryOwnership "codespace user has ownership over extension directory" # Node.js check "node" node --version check "nvm" bash -c ". /usr/local/share/nvm/nvm.sh && nvm --version" -check "nvs" bash -c ". /usr/local/nvs/nvs.sh && nvs --version" check "yarn" yarn --version check "npm" npm --version count=$(ls /usr/local/share/nvm/versions/node | wc -l) @@ -136,9 +135,10 @@ check "default-node-version" bash -c "node --version | grep 24." check "default-node-location" bash -c "which node | grep /home/codespace/nvm/current/bin" check "oryx-build-node-project" bash -c "oryx build ./sample/node" check "oryx-configured-current-node-version" bash -c "ls -la /home/codespace/nvm/current | grep /opt/nodejs" -check "nvm-install-node" bash -c ". /usr/local/share/nvm/nvm.sh && nvm install 8.0.0" -check "nvm-works-in-node-project" bash -c "node --version | grep v8.0.0" -check "default-node-location-remained-same" bash -c "which node | grep /home/codespace/nvm/current/bin" +check "nvm-switches-to-additional-node-version" bash -c ". /usr/local/share/nvm/nvm.sh && nvm use 22 && node --version | grep v22." +check "node-location-after-switching-to-22" bash -c "which node | grep /home/codespace/nvm/current/bin" +check "nvm-switches-back-to-default-node-version" bash -c ". /usr/local/share/nvm/nvm.sh && nvm use default && node --version | grep v24." +check "node-location-after-switching-back-to-default" bash -c "which node | grep /home/codespace/nvm/current/bin" # Ensures sdkman works in a Java Project check "default-java-version" bash -c "java --version" @@ -170,6 +170,8 @@ check "php-version-on-path-is-8.1.30" php --version | grep 8.1.30 # Test patches ls -la /home/codespace +check "python-3.13.8-pip-version" checkPythonPipVersion "/usr/local/python/3.13.8/bin/python" "26.1.0" +check "no-vulnerable-pip-cache-in-conda-pkgs" checkNoVulnerablePipCache "/opt/conda/pkgs" "26.1" ## Python - current checkPythonPackageVersion "python" "requests" "2.31.0"