From 3dab68ea7f8dece14e4f72b9050cbe524e45ae1d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 18:10:52 +0000 Subject: [PATCH 1/6] Initial plan From 2459634985b66e4ce04e346210fc198b554d7633 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 18:13:04 +0000 Subject: [PATCH 2/6] Add ohMyZshTheme option to common-utils feature Co-authored-by: the78mole <7723396+the78mole@users.noreply.github.com> --- src/common-utils/README.md | 1 + src/common-utils/devcontainer-feature.json | 5 +++++ src/common-utils/install.sh | 1 + src/common-utils/main.sh | 3 ++- 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/common-utils/README.md b/src/common-utils/README.md index 3d21e8b3f..e5218bdb4 100644 --- a/src/common-utils/README.md +++ b/src/common-utils/README.md @@ -19,6 +19,7 @@ Installs a set of common command line utilities, Oh My Zsh!, and sets up a non-r | configureZshAsDefaultShell | Change default shell to ZSH? | boolean | false | | installOhMyZsh | Install Oh My Zsh!? | boolean | true | | installOhMyZshConfig | Allow installing the default dev container .zshrc templates? | boolean | true | +| ohMyZshTheme | Oh My Zsh theme to use (e.g., 'robbyrussell', 'agnoster', 'fino'). Default is 'devcontainers'. | string | devcontainers | | upgradePackages | Upgrade OS packages? | boolean | true | | username | Enter name of a non-root user to configure or none to skip | string | automatic | | userUid | Enter UID for non-root user | string | automatic | diff --git a/src/common-utils/devcontainer-feature.json b/src/common-utils/devcontainer-feature.json index 14056e3a9..790a6d4b4 100644 --- a/src/common-utils/devcontainer-feature.json +++ b/src/common-utils/devcontainer-feature.json @@ -25,6 +25,11 @@ "default": true, "description": "Allow installing the default dev container .zshrc templates?" }, + "ohMyZshTheme": { + "type": "string", + "default": "devcontainers", + "description": "Oh My Zsh theme to use (e.g., 'robbyrussell', 'agnoster', 'fino'). Default is 'devcontainers'." + }, "upgradePackages": { "type": "boolean", "default": true, diff --git a/src/common-utils/install.sh b/src/common-utils/install.sh index 313271d0a..622d5ea72 100755 --- a/src/common-utils/install.sh +++ b/src/common-utils/install.sh @@ -13,6 +13,7 @@ INSTALL_ZSH="${INSTALLZSH:-"true"}" CONFIGURE_ZSH_AS_DEFAULT_SHELL="${CONFIGUREZSHASDEFAULTSHELL:-"false"}" INSTALL_OH_MY_ZSH="${INSTALLOHMYZSH:-"true"}" INSTALL_OH_MY_ZSH_CONFIG="${INSTALLOHMYZSHCONFIG:-"true"}" +OH_MY_ZSH_THEME="${OHMYZSHTHEME:-"devcontainers"}" UPGRADE_PACKAGES="${UPGRADEPACKAGES:-"true"}" USERNAME="${USERNAME:-"automatic"}" USER_UID="${UID:-"automatic"}" diff --git a/src/common-utils/main.sh b/src/common-utils/main.sh index b0fd2f3b0..f5f69a2b8 100644 --- a/src/common-utils/main.sh +++ b/src/common-utils/main.sh @@ -13,6 +13,7 @@ INSTALL_ZSH="${INSTALLZSH:-"true"}" CONFIGURE_ZSH_AS_DEFAULT_SHELL="${CONFIGUREZSHASDEFAULTSHELL:-"false"}" INSTALL_OH_MY_ZSH="${INSTALLOHMYZSH:-"true"}" INSTALL_OH_MY_ZSH_CONFIG="${INSTALLOHMYZSHCONFIG:-"true"}" +OH_MY_ZSH_THEME="${OHMYZSHTHEME:-"devcontainers"}" UPGRADE_PACKAGES="${UPGRADEPACKAGES:-"true"}" USERNAME="${USERNAME:-"automatic"}" USER_UID="${USERUID:-"automatic"}" @@ -570,7 +571,7 @@ if [ "${INSTALL_ZSH}" = "true" ]; then if ! [ -f "${template_path}" ] || ! grep -qF "$(head -n 1 "${template_path}")" "${user_rc_file}"; then echo -e "$(cat "${template_path}")\nzstyle ':omz:update' mode disabled" > ${user_rc_file} fi - sed -i -e 's/ZSH_THEME=.*/ZSH_THEME="devcontainers"/g' ${user_rc_file} + sed -i -e "s/ZSH_THEME=.*/ZSH_THEME=\"${OH_MY_ZSH_THEME}\"/g" ${user_rc_file} fi # Copy to non-root user if one is specified From fb457bd19533e31bdfd3f9e06e64a3aac70f528d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 18:14:38 +0000 Subject: [PATCH 3/6] Add test scenario for custom zsh theme Co-authored-by: the78mole <7723396+the78mole@users.noreply.github.com> --- test/common-utils/custom-zsh-theme.sh | 15 +++++++++++++++ test/common-utils/scenarios.json | 12 ++++++++++++ 2 files changed, 27 insertions(+) create mode 100755 test/common-utils/custom-zsh-theme.sh diff --git a/test/common-utils/custom-zsh-theme.sh b/test/common-utils/custom-zsh-theme.sh new file mode 100755 index 000000000..8449f9558 --- /dev/null +++ b/test/common-utils/custom-zsh-theme.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +# Definition specific tests +check "zsh" zsh --version +check "oh-my-zsh installed" test -d $HOME/.oh-my-zsh +check "zsh theme is fino" bash -c "cat ~/.zshrc | grep 'ZSH_THEME=\"fino\"'" +check "default shell is zsh" bash -c "getent passwd $(whoami) | awk -F: '{ print \$7 }' | grep '/bin/zsh'" + +# Report result +reportResults diff --git a/test/common-utils/scenarios.json b/test/common-utils/scenarios.json index ee138ca22..9c5137c20 100644 --- a/test/common-utils/scenarios.json +++ b/test/common-utils/scenarios.json @@ -284,5 +284,17 @@ "features": { "common-utils": {} } + }, + "custom-zsh-theme": { + "image": "ubuntu:jammy", + "remoteUser": "devcontainer", + "features": { + "common-utils": { + "installZsh": true, + "installOhMyZsh": true, + "ohMyZshTheme": "fino", + "configureZshAsDefaultShell": true + } + } } } \ No newline at end of file From 8d004ce687b36499bdaf4e0782785a19048ff13f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 18:15:41 +0000 Subject: [PATCH 4/6] Add validation for theme name to prevent injection attacks Co-authored-by: the78mole <7723396+the78mole@users.noreply.github.com> --- src/common-utils/main.sh | 8 +++++++- test/common-utils/custom-zsh-theme.sh | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/common-utils/main.sh b/src/common-utils/main.sh index f5f69a2b8..f41da3e59 100644 --- a/src/common-utils/main.sh +++ b/src/common-utils/main.sh @@ -571,7 +571,13 @@ if [ "${INSTALL_ZSH}" = "true" ]; then if ! [ -f "${template_path}" ] || ! grep -qF "$(head -n 1 "${template_path}")" "${user_rc_file}"; then echo -e "$(cat "${template_path}")\nzstyle ':omz:update' mode disabled" > ${user_rc_file} fi - sed -i -e "s/ZSH_THEME=.*/ZSH_THEME=\"${OH_MY_ZSH_THEME}\"/g" ${user_rc_file} + # Validate theme name to prevent injection (allow alphanumeric, hyphens, underscores, and dots) + if echo "${OH_MY_ZSH_THEME}" | grep -qE '^[a-zA-Z0-9_.-]+$'; then + sed -i -e "s/ZSH_THEME=.*/ZSH_THEME=\"${OH_MY_ZSH_THEME}\"/g" ${user_rc_file} + else + echo "Warning: Invalid theme name '${OH_MY_ZSH_THEME}'. Using default 'devcontainers' theme." + sed -i -e 's/ZSH_THEME=.*/ZSH_THEME="devcontainers"/g' ${user_rc_file} + fi fi # Copy to non-root user if one is specified diff --git a/test/common-utils/custom-zsh-theme.sh b/test/common-utils/custom-zsh-theme.sh index 8449f9558..6d8dd90da 100755 --- a/test/common-utils/custom-zsh-theme.sh +++ b/test/common-utils/custom-zsh-theme.sh @@ -8,7 +8,7 @@ source dev-container-features-test-lib # Definition specific tests check "zsh" zsh --version check "oh-my-zsh installed" test -d $HOME/.oh-my-zsh -check "zsh theme is fino" bash -c "cat ~/.zshrc | grep 'ZSH_THEME=\"fino\"'" +check "zsh theme is fino" grep 'ZSH_THEME="fino"' ~/.zshrc check "default shell is zsh" bash -c "getent passwd $(whoami) | awk -F: '{ print \$7 }' | grep '/bin/zsh'" # Report result From d91d9664a4d51445fd9d44e2866b2ed1c1aa0fea Mon Sep 17 00:00:00 2001 From: Daniel Glaser <7723396+the78mole@users.noreply.github.com> Date: Sun, 8 Mar 2026 13:20:47 +0100 Subject: [PATCH 5/6] Bump version from 2.5.7 to 2.5.8 --- src/common-utils/devcontainer-feature.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common-utils/devcontainer-feature.json b/src/common-utils/devcontainer-feature.json index 89d9a36d7..0cd9655a5 100644 --- a/src/common-utils/devcontainer-feature.json +++ b/src/common-utils/devcontainer-feature.json @@ -1,6 +1,6 @@ { "id": "common-utils", - "version": "2.5.7", + "version": "2.5.8", "name": "Common Utilities", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/common-utils", "description": "Installs a set of common command line utilities, Oh My Zsh!, and sets up a non-root user.", From 1ccb5ebc4a0a9a660c4d9e4ffc565802b7170625 Mon Sep 17 00:00:00 2001 From: Daniel Glaser <7723396+the78mole@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:01:28 +0000 Subject: [PATCH 6/6] Add support for additional non-root user groups in common-utils feature --- src/common-utils/README.md | 1 + src/common-utils/devcontainer-feature.json | 7 ++++++- src/common-utils/install.sh | 1 + src/common-utils/main.sh | 17 +++++++++++++++++ test/common-utils/add-groups.sh | 13 +++++++++++++ test/common-utils/scenarios.json | 9 +++++++++ 6 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 test/common-utils/add-groups.sh diff --git a/src/common-utils/README.md b/src/common-utils/README.md index e5218bdb4..25538d9ef 100644 --- a/src/common-utils/README.md +++ b/src/common-utils/README.md @@ -24,6 +24,7 @@ Installs a set of common command line utilities, Oh My Zsh!, and sets up a non-r | username | Enter name of a non-root user to configure or none to skip | string | automatic | | userUid | Enter UID for non-root user | string | automatic | | userGid | Enter GID for non-root user | string | automatic | +| addGroups | Comma-separated list of additional groups to add the non-root user to. Groups are created if needed. | string | | | nonFreePackages | Add packages from non-free Debian repository? (Debian only) | boolean | false | ## OS Support diff --git a/src/common-utils/devcontainer-feature.json b/src/common-utils/devcontainer-feature.json index 0cd9655a5..b3c136044 100644 --- a/src/common-utils/devcontainer-feature.json +++ b/src/common-utils/devcontainer-feature.json @@ -1,6 +1,6 @@ { "id": "common-utils", - "version": "2.5.8", + "version": "2.5.9", "name": "Common Utilities", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/common-utils", "description": "Installs a set of common command line utilities, Oh My Zsh!, and sets up a non-root user.", @@ -65,6 +65,11 @@ "default": "automatic", "description": "Enter GID for non-root user" }, + "addGroups": { + "type": "string", + "default": "", + "description": "Comma-separated list of additional groups to add the non-root user to. Groups are created if needed." + }, "nonFreePackages": { "type": "boolean", "default": false, diff --git a/src/common-utils/install.sh b/src/common-utils/install.sh index 622d5ea72..274623f0d 100755 --- a/src/common-utils/install.sh +++ b/src/common-utils/install.sh @@ -18,6 +18,7 @@ UPGRADE_PACKAGES="${UPGRADEPACKAGES:-"true"}" USERNAME="${USERNAME:-"automatic"}" USER_UID="${UID:-"automatic"}" USER_GID="${GID:-"automatic"}" +ADD_GROUPS="${ADDGROUPS:-""}" ADD_NON_FREE_PACKAGES="${NONFREEPACKAGES:-"false"}" INSTALL_SSL="${INSTALLSSL:-"true"}" diff --git a/src/common-utils/main.sh b/src/common-utils/main.sh index 79f13fc54..96adf8dca 100644 --- a/src/common-utils/main.sh +++ b/src/common-utils/main.sh @@ -18,6 +18,7 @@ UPGRADE_PACKAGES="${UPGRADEPACKAGES:-"true"}" USERNAME="${USERNAME:-"automatic"}" USER_UID="${USERUID:-"automatic"}" USER_GID="${USERGID:-"automatic"}" +ADD_GROUPS="${ADDGROUPS:-""}" ADD_NON_FREE_PACKAGES="${NONFREEPACKAGES:-"false"}" INSTALL_SSL="${INSTALLSSL:-"true"}" @@ -440,6 +441,22 @@ else fi fi +if [ "${USERNAME}" != "root" ] && [ -n "${ADD_GROUPS}" ]; then + IFS=',' read -ra EXTRA_GROUPS <<< "${ADD_GROUPS}" + for extra_group in "${EXTRA_GROUPS[@]}"; do + extra_group="$(echo "${extra_group}" | xargs)" + if [ -z "${extra_group}" ]; then + continue + fi + + if ! getent group "${extra_group}" > /dev/null 2>&1; then + groupadd "${extra_group}" + fi + + usermod -a -G "${extra_group}" "${USERNAME}" + done +fi + # Add add sudo support for non-root user if [ "${USERNAME}" != "root" ] && [ "${EXISTING_NON_ROOT_USER}" != "${USERNAME}" ]; then echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME diff --git a/test/common-utils/add-groups.sh b/test/common-utils/add-groups.sh new file mode 100644 index 000000000..c0e14372e --- /dev/null +++ b/test/common-utils/add-groups.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +source dev-container-features-test-lib + +check "non-root user" id alternate +check "dialout group exists" getent group dialout +check "plugdev group exists" getent group plugdev +check "alternate in dialout" bash -lc "id -nG alternate | tr ' ' '\n' | grep -Fx dialout" +check "alternate in plugdev" bash -lc "id -nG alternate | tr ' ' '\n' | grep -Fx plugdev" + +reportResults \ No newline at end of file diff --git a/test/common-utils/scenarios.json b/test/common-utils/scenarios.json index 9c5137c20..7a1f11c01 100644 --- a/test/common-utils/scenarios.json +++ b/test/common-utils/scenarios.json @@ -126,6 +126,15 @@ } } }, + "add-groups": { + "image": "debian:bullseye", + "features": { + "common-utils": { + "username": "alternate", + "addGroups": "dialout,plugdev" + } + } + }, "username-default": { "image": "debian:bullseye", "features": {