Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't use mutable objects as default arguments #161

Closed
benjdevries opened this issue Mar 26, 2024 · 3 comments · Fixed by #162
Closed

Don't use mutable objects as default arguments #161

benjdevries opened this issue Mar 26, 2024 · 3 comments · Fixed by #162

Comments

@benjdevries
Copy link

pyotp.random_base32 and pyotp.random_hex use list("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567") and list("ABCDEF0123456789") respectively as default arguments. While it does appear that this specific usage is safe, using mutable objects as default arguments is a recipe for disaster since the objects will be instantiated at module load and the reference will be shared between any calls to that module.

Consider the following dangerous usage where calling foo prints a different result every time:

def foo(bar=list("1234")):
    print(bar)
    bar.pop()

foo()  # ['1', '2', '3', '4'] 
foo()  # ['1', '2', '3'] 
foo()  # ['1', '2'] 
foo()  # ['1']
foo()  # []
foo()  # IndexError  

From the looks of it, there is no reason these arguments need to be lists. They exist only to be passed to random.choice and are typed as Sequence[str] which would be satisfied by simply using the strings "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" and "ABCDEF0123456789" directly as default arguments. And if they do need to be list objects, they should have a default value of None with a conditional check that assigns the variable within the function body, e.g.

def random_base32(length: int = 32, chars: Sequence[str] = None -> str:
    if chars is None:
        chars = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")
    ...
@kislyuk
Copy link
Member

kislyuk commented Mar 26, 2024

As you point out, this usage is safe because the functions in question don't mutate, return, or expose the value of the argument. However, for the avoidance of confusion I agree that it would be great to make this argument immutable with frozenset(). Would you like to do this in a PR?

@benjdevries
Copy link
Author

Yes, I can add a PR. Is there a reason you suggested frozenset() over just using the strings directly, which are already immutable sequences of characters?

@kislyuk
Copy link
Member

kislyuk commented Mar 26, 2024

Good point, we can use strings directly as the argument values. For legibility, I would like to keep the value directly in the signature instead of setting it to None.

kislyuk pushed a commit that referenced this issue Mar 26, 2024
Fixes #161 

Avoid using mutable objects as default arguments as functions. Even
though this particular usage was safe, it opens up the projects to
issues down the road if the default argument is mutated within the
function. For this usage, we don't need an actual `list` object, just a
sequence of strings, so we can use a string itself.
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 a pull request may close this issue.

2 participants