From 14c0ac92207bdc8d987235ce232f2d0c7a496819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9=20=D0=A1=D0=B0?= =?UTF-8?q?=D0=B1=D0=B8=D0=BB=D0=B8=D0=BD?= Date: Fri, 10 Jul 2026 19:29:17 +0300 Subject: [PATCH] Fix Unicode env parsing in infra_secrets for stage startup. Use unicode:characters_to_binary for os:getenv values so Cyrillic placeholder passwords fail with weak_secret instead of badarg crash. Co-authored-by: Cursor --- src/infra/infra_secrets.erl | 10 +++++++--- test/unit/infra_secrets_tests.erl | 8 +++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/infra/infra_secrets.erl b/src/infra/infra_secrets.erl index 6fe7fd3..b6d55c0 100644 --- a/src/infra/infra_secrets.erl +++ b/src/infra/infra_secrets.erl @@ -51,7 +51,7 @@ require_admin_seed_email(EnvKey) -> case os:getenv(EnvKey) of false -> {error, {missing_secret, list_to_atom(EnvKey)}}; "" -> {error, {missing_secret, list_to_atom(EnvKey)}}; - Email -> {ok, list_to_binary(Email)} + Email -> {ok, env_to_binary(Email)} end. -spec require_admin_seed_password(string()) -> @@ -61,7 +61,7 @@ require_admin_seed_password(EnvKey) -> false -> {error, {missing_secret, list_to_atom(EnvKey)}}; "" -> {error, {missing_secret, list_to_atom(EnvKey)}}; Pass -> - Bin = list_to_binary(Pass), + Bin = env_to_binary(Pass), case is_strong_secret(Bin) of true -> {ok, Bin}; false -> {error, {weak_secret, list_to_atom(EnvKey)}} @@ -124,9 +124,13 @@ os_getenv_binary(Key) -> case os:getenv(Key) of false -> undefined; "" -> undefined; - Val -> list_to_binary(Val) + Val -> env_to_binary(Val) end. +%% os:getenv/1 returns Unicode charlists; list_to_binary/1 only accepts byte iolists. +env_to_binary(Val) when is_binary(Val) -> Val; +env_to_binary(Val) when is_list(Val) -> unicode:characters_to_binary(Val). + dev_warn_if_weak(_Name, undefined) -> ok; dev_warn_if_weak(Name, Secret) -> case is_strong_secret(Secret) of diff --git a/test/unit/infra_secrets_tests.erl b/test/unit/infra_secrets_tests.erl index 61e4263..5816b91 100644 --- a/test/unit/infra_secrets_tests.erl +++ b/test/unit/infra_secrets_tests.erl @@ -26,7 +26,8 @@ infra_secrets_test_() -> {"stage rejects missing JWT_SECRET", fun test_stage_missing_jwt/0}, {"stage rejects weak JWT_SECRET", fun test_stage_weak_jwt/0}, {"stage accepts strong secrets", fun test_stage_strong_ok/0}, - {"require_admin_seed_password rejects 123456", fun test_weak_admin_password/0} + {"require_admin_seed_password rejects 123456", fun test_weak_admin_password/0}, + {"require_admin_seed_password rejects cyrillic placeholder", fun test_cyrillic_placeholder_password/0} ]}. test_dev_missing_ok() -> @@ -60,3 +61,8 @@ test_weak_admin_password() -> os:putenv("ADMIN_PASSWORD", "123456"), ?assertMatch({error, {weak_secret, 'ADMIN_PASSWORD'}}, infra_secrets:require_admin_seed_password("ADMIN_PASSWORD")). + +test_cyrillic_placeholder_password() -> + os:putenv("ADMIN_PASSWORD", "замените"), + ?assertMatch({error, {weak_secret, 'ADMIN_PASSWORD'}}, + infra_secrets:require_admin_seed_password("ADMIN_PASSWORD")).