Skip to content

fix(config): validate env var overrides and re-validate config (issue #13) - #33

Merged
labtgbot merged 1 commit into
mainfrom
issue-13-config-env-validation
Jun 15, 2026
Merged

fix(config): validate env var overrides and re-validate config (issue #13)#33
labtgbot merged 1 commit into
mainfrom
issue-13-config-env-validation

Conversation

@xdevrobot

Copy link
Copy Markdown
Collaborator

Summary

Fixes issue #13 — HIGH severity CWE-20: Config env var overrides bypass Zod schema validation entirely.

TELETON_* environment variables were applied directly to the parsed config object after Zod validation, with no further validation. This allowed injection of arbitrary values (empty strings, invalid ports, non-loopback hosts, non-HTTPS base URLs) that bypass all schema constraints.

Root Cause

Config validation ran once with ConfigSchema.safeParse(raw) (line 95), then env vars directly mutated the parsed object. Zod validation was never re-run — the "split brain" anti-pattern where validated config and runtime config diverge silently.

Changes

src/config/loader.ts

Before After
if (process.env.X) — empty string "" silently ignored if (process.env.X !== undefined) — empty string caught and validated
TELETON_API_KEY — no format check Min-length 8 chars, empty string rejected
TELETON_TG_API_HASH, TELETON_TG_PHONE — no empty check Empty string rejected
TELETON_WEBUI_ENABLED — any truthy value = true Strict "true"/"false" (case-insensitive)
TELETON_API_ENABLED — any truthy value = true Strict "true"/"false" (case-insensitive)
TELETON_WEBUI_PORT — invalid silently ignored parsePort() helper throws on invalid/out-of-range
TELETON_WEBUI_HOST=0.0.0.0 — only warned Hard-blocked without auth_token
TELETON_BASE_URL — any valid URL accepted Scheme must be http or https
No re-validation after overrides ConfigSchema.safeParse(config) re-validates final config
No TELETON_WEBUI_AUTH_TOKEN env var Added with empty-string validation
TELETON_TAVILY_API_KEY, TELETON_TONAPI_KEY, TELETON_TONCENTER_API_KEY — no validation Empty string rejected

src/config/__tests__/loader.test.ts

  • Updated 2 existing tests to match new validation behavior (invalid port throws, non-loopback blocked)
  • Added 17 new test cases covering:
    • Empty string rejection for all API key env vars
    • Invalid TELETON_WEBUI_ENABLED / TELETON_API_ENABLED values
    • Port range validation (below 1024, above 65535, non-numeric)
    • Non-loopback host blocking without auth_token
    • Loopback host allowed without auth_token
    • Non-HTTPS base URL rejection
    • Case-insensitive boolean parsing

Verification

npx vitest run src/config/__tests__/loader.test.ts
# 71 tests passed ✓

Security Impact

Attack Vector Before After
TELETON_API_KEY="" Silently ignored ❌ Empty string rejected
TELETON_API_KEY="short" Accepted ❌ Too short (< 8 chars)
TELETON_WEBUI_ENABLED=maybe config.webui.enabled = false ❌ Must be "true"/"false"
TELETON_WEBUI_PORT=22 Accepted ❌ Port < 1024 rejected
TELETON_WEBUI_HOST=0.0.0.0 ✅ Bound to all interfaces ❌ Blocked without auth_token
TELETON_BASE_URL=ftp://evil.com ✅ Accepted ❌ Scheme must be http/https
TELETON_TONAPI_KEY="" Silently ignored ❌ Empty string rejected
Config divergence after overrides Silent ❌ Re-validated, throws on mismatch

🤖 Generated with Claude Code

…13)

Prevents CWE-20 bypass of Zod schema validation through TELETON_* env vars.
Env var overrides now apply the same constraints as the schema before
assignment, and the full config is re-validated after all overrides.

Changes:
- loader.ts: Replace truthy checks with `!== undefined` so empty strings
  are caught and validated instead of silently ignored
- loader.ts: Add min-length check for TELETON_API_KEY (≥8 chars)
- loader.ts: Add empty-string rejection for TELETON_TG_API_HASH,
  TELETON_TG_PHONE, TELETON_WEBUI_AUTH_TOKEN, TELETON_TAVILY_API_KEY,
  TELETON_TONAPI_KEY, TELETON_TONCENTER_API_KEY
- loader.ts: Validate TELETON_WEBUI_ENABLED and TELETON_API_ENABLED are
  strictly "true"/"false" (case-insensitive)
- loader.ts: Extract parsePort() helper enforcing 1024-65535 range,
  throw on invalid values instead of silent ignore
- loader.ts: Hard-block non-loopback TELETON_WEBUI_HOST when
  auth_token is not set (was only a warning)
- loader.ts: Validate TELETON_BASE_URL scheme is http/https
- loader.ts: Add TELETON_WEBUI_AUTH_TOKEN env var override
- loader.ts: Re-validate full config via ConfigSchema.safeParse() after
  all env var overrides to catch any divergence
- loader.test.ts: Update tests for new validation behavior, add 17 new
  test cases covering empty strings, invalid values, port ranges,
  non-loopback blocking, and scheme validation

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@labtgbot
labtgbot merged commit b20ca47 into main Jun 15, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HIGH][CWE-20] Config env var overrides bypass Zod schema validation entirely

2 participants