From 7738e7032a259cf7cdede7c430bf82ebd9676ff4 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 19 Mar 2026 17:53:56 -0500 Subject: [PATCH] fix: enforce env/_FILE mutual exclusivity for empty values The _FILE mutual-exclusivity guard silently passed when the plain env var was set to an empty string, because empty strings are falsy. Python: changed `if value and file_path` to `if value is not None and file_path is not None`, and the fallback return from `value or default` to `value if value is not None else default`. entrypoint.sh: changed ${!var:-} to ${!var+set} to test whether the variable is defined at all, not just non-empty. entrypoint-slim.sh: changed -n tests to ${var+set} via eval, matching the bash entrypoint semantics. --- CHANGELOG.md | 6 ++++++ entrypoint-slim.sh | 4 +++- entrypoint.sh | 2 +- open_terminal/env.py | 4 ++-- pyproject.toml | 2 +- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42c217a..4347206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [0.11.23] - 2026-03-19 + +### Fixed + +- 🔐 **`_FILE` mutual exclusivity bypassed by empty env vars** — setting e.g. `OPEN_TERMINAL_API_KEY=""` alongside `OPEN_TERMINAL_API_KEY_FILE` silently skipped the conflict check because empty strings are falsy. The Python helper (`_resolve_file_env`), `entrypoint.sh`, and `entrypoint-slim.sh` now test whether the variable is *set* (not merely non-empty), so any explicit assignment — including `=""` — correctly triggers the mutual-exclusivity error. + ## [0.11.22] - 2026-03-19 ### Fixed diff --git a/entrypoint-slim.sh b/entrypoint-slim.sh index 7bd3d3b..8bee808 100755 --- a/entrypoint-slim.sh +++ b/entrypoint-slim.sh @@ -32,8 +32,10 @@ file_env() { local val="$def" eval local currentVal="\${$var:-}" eval local fileVal="\${$fileVar:-}" + eval local varIsSet="\${$var+set}" + eval local fileIsSet="\${$fileVar+set}" - if [ -n "$currentVal" ] && [ -n "$fileVal" ]; then + if [ "$varIsSet" = "set" ] && [ "$fileIsSet" = "set" ]; then printf >&2 'error: both %s and %s are set (but are exclusive)\n' "$var" "$fileVar" exit 1 fi diff --git a/entrypoint.sh b/entrypoint.sh index 20bfdb7..50b7601 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -9,7 +9,7 @@ file_env() { local var="$1" local fileVar="${var}_FILE" local def="${2:-}" - if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then + if [ "${!var+set}" = "set" ] && [ "${!fileVar+set}" = "set" ]; then printf >&2 'error: both %s and %s are set (but are exclusive)\n' "$var" "$fileVar" exit 1 fi diff --git a/open_terminal/env.py b/open_terminal/env.py index dd4a1de..2bc84ab 100644 --- a/open_terminal/env.py +++ b/open_terminal/env.py @@ -16,7 +16,7 @@ def _resolve_file_env(var: str, default: str = "") -> str: value = os.environ.get(var) file_path = os.environ.get(f"{var}_FILE") - if value and file_path: + if value is not None and file_path is not None: raise ValueError( f"Both {var} and {var}_FILE are set, but they are mutually exclusive." ) @@ -25,7 +25,7 @@ def _resolve_file_env(var: str, default: str = "") -> str: with open(file_path) as f: return f.read().strip() - return value or default + return value if value is not None else default API_KEY = _resolve_file_env("OPEN_TERMINAL_API_KEY", config.get("api_key", "")) diff --git a/pyproject.toml b/pyproject.toml index 313fd73..4ca70ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "open-terminal" -version = "0.11.22" +version = "0.11.23" description = "A remote terminal API." readme = "README.md" authors = [