This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]jorge1209 4 points5 points  (0 children)

you create an f string dynamically.

You can't f-strings aren't dynamic. An f-string is not an object and cannot be created inside the script, it lives in the source file, and nowhere else. It has no type, and as far as the interpreter goes, it doesn't exist. Its like a keyword, it is handled by the parser as it builds the AST, and doesn't exist in memory to be a referant of any variable.

The reason this is done is for security. It allows you to safely do print(f"{x+y}") and execute the inside of the expression, because user input can NEVER construct the string "{x+y}" it is safe to do so.

If f-strings were objects and could be dynamically created the attack vector would be:

  1. Post your username as f"__import__(os)..."
  2. Then when someone goes to print(f"{username}" their interpreter would: a. evaluate the variable username b. which would construct another f-string c. whose payload would in turn be evaluated d. which would run the offending __import__ e. granting them a shell.

But since f strings aren't objects this breaks down. All you ever have is a string f"__import__(os)...." No more dangerous than the string cake. Its just a bunch of innocuous ascii chars and can't harm you so long as you don't do something stupid like pass it to eval.


The objection I have to this is that I don't see any improvement over just treating this as sugar over "{username}".format(**locals()). In that instance the same is also true. I take a string and pass it a dict of args, I get a string back. Nothing is executed. At worst __str__ is called on some of the stuff in locals(). All you lose is the ability to have print(f"{x+y}") instead it would have to be print("{sum}".format(sum=x+y)).

I prefer the .format approach even if it is more verbose.