I think this and similar security issues can be solved by using the fact that endwhile always occurs first on a line, optionally prepended by spaces, and therefore we could add "^ *" at start of the pattern. This ensures that it will only be run once per line.
endwhile_text = re.sub(
r"^( *)endwhile \([^()]*?\)",
f"\1endwhile ({breakstatement})",
endwhile_text,
flags=re.DOTALL,
)