This is an automated email from the ASF dual-hosted git repository.
vincbeck pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 21332eab89a Use secrets for fab users `--use-random-password`
generation (#72092)
21332eab89a is described below
commit 21332eab89ad01bbeecf6eef2be4ca129f08dc81
Author: Samina <[email protected]>
AuthorDate: Wed Aug 26 19:16:59 2026 +0530
Use secrets for fab users `--use-random-password` generation (#72092)
random.choices draws from the Mersenne Twister PRNG that Python documents
as unfit for security use, so the generated web-login credential is
predictable, and string.printable also seeds the value with whitespace and
control characters that land in the stored password.
---
.../fab/auth_manager/cli_commands/user_command.py | 5 +++--
.../fab/auth_manager/cli_commands/test_user_command.py | 16 ++++++++++++++++
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git
a/providers/fab/src/airflow/providers/fab/auth_manager/cli_commands/user_command.py
b/providers/fab/src/airflow/providers/fab/auth_manager/cli_commands/user_command.py
index 4ae060e144f..adecdb458ff 100644
---
a/providers/fab/src/airflow/providers/fab/auth_manager/cli_commands/user_command.py
+++
b/providers/fab/src/airflow/providers/fab/auth_manager/cli_commands/user_command.py
@@ -22,8 +22,8 @@ import functools
import getpass
import json
import os
-import random
import re
+import secrets
import string
from typing import Any
@@ -113,7 +113,8 @@ def user_reset_password(args):
def _create_password(args):
if args.use_random_password:
- password = "".join(random.choices(string.printable, k=16))
+ characters = string.ascii_letters + string.digits + string.punctuation
+ password = "".join(secrets.choice(characters) for _ in range(16))
elif args.password:
password = args.password
else:
diff --git
a/providers/fab/tests/unit/fab/auth_manager/cli_commands/test_user_command.py
b/providers/fab/tests/unit/fab/auth_manager/cli_commands/test_user_command.py
index 0368d5b83ea..ef2664c8e9b 100644
---
a/providers/fab/tests/unit/fab/auth_manager/cli_commands/test_user_command.py
+++
b/providers/fab/tests/unit/fab/auth_manager/cli_commands/test_user_command.py
@@ -16,13 +16,17 @@
# under the License.
from __future__ import annotations
+import argparse
import json
import os
import re
+import secrets
+import string
import tempfile
from contextlib import redirect_stdout
from importlib import reload
from io import StringIO
+from unittest import mock
import pytest
@@ -39,6 +43,18 @@ TEST_USER2_EMAIL = "[email protected]"
TEST_USER3_EMAIL = "[email protected]"
[email protected](
+
"airflow.providers.fab.auth_manager.cli_commands.user_command.secrets.choice",
+ side_effect=secrets.choice,
+)
+def test_create_password_uses_csprng_without_whitespace(mock_choice):
+ args = argparse.Namespace(use_random_password=True, password=None)
+ password = user_command._create_password(args)
+ assert mock_choice.call_count == 16
+ assert len(password) == 16
+ assert not any(char in string.whitespace for char in password)
+
+
def _does_user_belong_to_role(appbuilder, email, rolename):
user = appbuilder.sm.find_user(email=email)
role = appbuilder.sm.find_role(rolename)