prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Increase default work factor for PBKDF2 to 600,000 iterations
  https://github.com/pallets/werkzeug/blob/bba2109e276ab876b8d64e2b967412cb6da1d865/src/werkzeug/security.py#L12
  
  The value has not been updated for a few years. I propose increasing it to the same number as the next release of Django will use.
  
  See https://github.com/django/django/pull/16521 and https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `src/werkzeug/security.py`
  Content:
  ```
  1 import hashlib
  2 import hmac
  3 import os
  4 import posixpath
  5 import secrets
  6 import typing as t
  7 
  8 if t.TYPE_CHECKING:
  9     pass
  10 
  11 SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  12 DEFAULT_PBKDF2_ITERATIONS = 260000
  13 
  14 _os_alt_seps: t.List[str] = list(
  15     sep for sep in [os.sep, os.path.altsep] if sep is not None and sep != "/"
  16 )
  17 
  18 
  19 def gen_salt(length: int) -> str:
  20     """Generate a random string of SALT_CHARS with specified ``length``."""
  21     if length <= 0:
  22         raise ValueError("Salt length must be positive")
  23 
  24     return "".join(secrets.choice(SALT_CHARS) for _ in range(length))
  25 
  26 
  27 def _hash_internal(method: str, salt: str, password: str) -> t.Tuple[str, str]:
  28     """Internal password hash helper.  Supports plaintext without salt,
  29     unsalted and salted passwords.  In case salted passwords are used
  30     hmac is used.
  31     """
  32     if method == "plain":
  33         return password, method
  34 
  35     salt = salt.encode("utf-8")
  36     password = password.encode("utf-8")
  37 
  38     if method.startswith("pbkdf2:"):
  39         if not salt:
  40             raise ValueError("Salt is required for PBKDF2")
  41 
  42         args = method[7:].split(":")
  43 
  44         if len(args) not in (1, 2):
  45             raise ValueError("Invalid number of arguments for PBKDF2")
  46 
  47         method = args.pop(0)
  48         iterations = int(args[0] or 0) if args else DEFAULT_PBKDF2_ITERATIONS
  49         return (
  50             hashlib.pbkdf2_hmac(method, password, salt, iterations).hex(),
  51             f"pbkdf2:{method}:{iterations}",
  52         )
  53 
  54     if salt:
  55         return hmac.new(salt, password, method).hexdigest(), method
  56 
  57     return hashlib.new(method, password).hexdigest(), method
  58 
  59 
  60 def generate_password_hash(
  61     password: str, method: str = "pbkdf2:sha256", salt_length: int = 16
  62 ) -> str:
  63     """Hash a password with the given method and salt with a string of
  64     the given length. The format of the string returned includes the method
  65     that was used so that :func:`check_password_hash` can check the hash.
  66 
  67     The format for the hashed string looks like this::
  68 
  69         method$salt$hash
  70 
  71     This method can **not** generate unsalted passwords but it is possible
  72     to set param method='plain' in order to enforce plaintext passwords.
  73     If a salt is used, hmac is used internally to salt the password.
  74 
  75     If PBKDF2 is wanted it can be enabled by setting the method to
  76     ``pbkdf2:method:iterations`` where iterations is optional::
  77 
  78         pbkdf2:sha256:80000$salt$hash
  79         pbkdf2:sha256$salt$hash
  80 
  81     :param password: the password to hash.
  82     :param method: the hash method to use (one that hashlib supports). Can
  83                    optionally be in the format ``pbkdf2:method:iterations``
  84                    to enable PBKDF2.
  85     :param salt_length: the length of the salt in letters.
  86     """
  87     salt = gen_salt(salt_length) if method != "plain" else ""
  88     h, actual_method = _hash_internal(method, salt, password)
  89     return f"{actual_method}${salt}${h}"
  90 
  91 
  92 def check_password_hash(pwhash: str, password: str) -> bool:
  93     """Check a password against a given salted and hashed password value.
  94     In order to support unsalted legacy passwords this method supports
  95     plain text passwords, md5 and sha1 hashes (both salted and unsalted).
  96 
  97     Returns `True` if the password matched, `False` otherwise.
  98 
  99     :param pwhash: a hashed string like returned by
  100                    :func:`generate_password_hash`.
  101     :param password: the plaintext password to compare against the hash.
  102     """
  103     if pwhash.count("$") < 2:
  104         return False
  105 
  106     method, salt, hashval = pwhash.split("$", 2)
  107     return hmac.compare_digest(_hash_internal(method, salt, password)[0], hashval)
  108 
  109 
  110 def safe_join(directory: str, *pathnames: str) -> t.Optional[str]:
  111     """Safely join zero or more untrusted path components to a base
  112     directory to avoid escaping the base directory.
  113 
  114     :param directory: The trusted base directory.
  115     :param pathnames: The untrusted path components relative to the
  116         base directory.
  117     :return: A safe path, otherwise ``None``.
  118     """
  119     if not directory:
  120         # Ensure we end up with ./path if directory="" is given,
  121         # otherwise the first untrusted part could become trusted.
  122         directory = "."
  123 
  124     parts = [directory]
  125 
  126     for filename in pathnames:
  127         if filename != "":
  128             filename = posixpath.normpath(filename)
  129 
  130         if (
  131             any(sep in filename for sep in _os_alt_seps)
  132             or os.path.isabs(filename)
  133             or filename == ".."
  134             or filename.startswith("../")
  135         ):
  136             return None
  137 
  138         parts.append(filename)
  139 
  140     return posixpath.join(*parts)
  141
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/src/werkzeug/security.py b/src/werkzeug/security.py
  --- a/src/werkzeug/security.py
  +++ b/src/werkzeug/security.py
  @@ -9,7 +9,7 @@
       pass
   
   SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  -DEFAULT_PBKDF2_ITERATIONS = 260000
  +DEFAULT_PBKDF2_ITERATIONS = 600000
   
   _os_alt_seps: t.List[str] = list(
       sep for sep in [os.sep, os.path.altsep] if sep is not None and sep != "/"
  
verification_info: {"golden_diff": "diff --git a/src/werkzeug/security.py b/src/werkzeug/security.py\n--- a/src/werkzeug/security.py\n+++ b/src/werkzeug/security.py\n@@ -9,7 +9,7 @@\n     pass\n \n SALT_CHARS = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\n-DEFAULT_PBKDF2_ITERATIONS = 260000\n+DEFAULT_PBKDF2_ITERATIONS = 600000\n \n _os_alt_seps: t.List[str] = list(\n     sep for sep in [os.sep, os.path.altsep] if sep is not None and sep != \"/\"\n", "issue": "Increase default work factor for PBKDF2 to 600,000 iterations\nhttps://github.com/pallets/werkzeug/blob/bba2109e276ab876b8d64e2b967412cb6da1d865/src/werkzeug/security.py#L12\r\n\r\nThe value has not been updated for a few years. I propose increasing it to the same number as the next release of Django will use.\r\n\r\nSee https://github.com/django/django/pull/16521 and https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2.\n", "before_files": [{"content": "import hashlib\nimport hmac\nimport os\nimport posixpath\nimport secrets\nimport typing as t\n\nif t.TYPE_CHECKING:\n    pass\n\nSALT_CHARS = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\nDEFAULT_PBKDF2_ITERATIONS = 260000\n\n_os_alt_seps: t.List[str] = list(\n    sep for sep in [os.sep, os.path.altsep] if sep is not None and sep != \"/\"\n)\n\n\ndef gen_salt(length: int) -> str:\n    \"\"\"Generate a random string of SALT_CHARS with specified ``length``.\"\"\"\n    if length <= 0:\n        raise ValueError(\"Salt length must be positive\")\n\n    return \"\".join(secrets.choice(SALT_CHARS) for _ in range(length))\n\n\ndef _hash_internal(method: str, salt: str, password: str) -> t.Tuple[str, str]:\n    \"\"\"Internal password hash helper.  Supports plaintext without salt,\n    unsalted and salted passwords.  In case salted passwords are used\n    hmac is used.\n    \"\"\"\n    if method == \"plain\":\n        return password, method\n\n    salt = salt.encode(\"utf-8\")\n    password = password.encode(\"utf-8\")\n\n    if method.startswith(\"pbkdf2:\"):\n        if not salt:\n            raise ValueError(\"Salt is required for PBKDF2\")\n\n        args = method[7:].split(\":\")\n\n        if len(args) not in (1, 2):\n            raise ValueError(\"Invalid number of arguments for PBKDF2\")\n\n        method = args.pop(0)\n        iterations = int(args[0] or 0) if args else DEFAULT_PBKDF2_ITERATIONS\n        return (\n            hashlib.pbkdf2_hmac(method, password, salt, iterations).hex(),\n            f\"pbkdf2:{method}:{iterations}\",\n        )\n\n    if salt:\n        return hmac.new(salt, password, method).hexdigest(), method\n\n    return hashlib.new(method, password).hexdigest(), method\n\n\ndef generate_password_hash(\n    password: str, method: str = \"pbkdf2:sha256\", salt_length: int = 16\n) -> str:\n    \"\"\"Hash a password with the given method and salt with a string of\n    the given length. The format of the string returned includes the method\n    that was used so that :func:`check_password_hash` can check the hash.\n\n    The format for the hashed string looks like this::\n\n        method$salt$hash\n\n    This method can **not** generate unsalted passwords but it is possible\n    to set param method='plain' in order to enforce plaintext passwords.\n    If a salt is used, hmac is used internally to salt the password.\n\n    If PBKDF2 is wanted it can be enabled by setting the method to\n    ``pbkdf2:method:iterations`` where iterations is optional::\n\n        pbkdf2:sha256:80000$salt$hash\n        pbkdf2:sha256$salt$hash\n\n    :param password: the password to hash.\n    :param method: the hash method to use (one that hashlib supports). Can\n                   optionally be in the format ``pbkdf2:method:iterations``\n                   to enable PBKDF2.\n    :param salt_length: the length of the salt in letters.\n    \"\"\"\n    salt = gen_salt(salt_length) if method != \"plain\" else \"\"\n    h, actual_method = _hash_internal(method, salt, password)\n    return f\"{actual_method}${salt}${h}\"\n\n\ndef check_password_hash(pwhash: str, password: str) -> bool:\n    \"\"\"Check a password against a given salted and hashed password value.\n    In order to support unsalted legacy passwords this method supports\n    plain text passwords, md5 and sha1 hashes (both salted and unsalted).\n\n    Returns `True` if the password matched, `False` otherwise.\n\n    :param pwhash: a hashed string like returned by\n                   :func:`generate_password_hash`.\n    :param password: the plaintext password to compare against the hash.\n    \"\"\"\n    if pwhash.count(\"$\") < 2:\n        return False\n\n    method, salt, hashval = pwhash.split(\"$\", 2)\n    return hmac.compare_digest(_hash_internal(method, salt, password)[0], hashval)\n\n\ndef safe_join(directory: str, *pathnames: str) -> t.Optional[str]:\n    \"\"\"Safely join zero or more untrusted path components to a base\n    directory to avoid escaping the base directory.\n\n    :param directory: The trusted base directory.\n    :param pathnames: The untrusted path components relative to the\n        base directory.\n    :return: A safe path, otherwise ``None``.\n    \"\"\"\n    if not directory:\n        # Ensure we end up with ./path if directory=\"\" is given,\n        # otherwise the first untrusted part could become trusted.\n        directory = \".\"\n\n    parts = [directory]\n\n    for filename in pathnames:\n        if filename != \"\":\n            filename = posixpath.normpath(filename)\n\n        if (\n            any(sep in filename for sep in _os_alt_seps)\n            or os.path.isabs(filename)\n            or filename == \"..\"\n            or filename.startswith(\"../\")\n        ):\n            return None\n\n        parts.append(filename)\n\n    return posixpath.join(*parts)\n", "path": "src/werkzeug/security.py"}], "after_files": [{"content": "import hashlib\nimport hmac\nimport os\nimport posixpath\nimport secrets\nimport typing as t\n\nif t.TYPE_CHECKING:\n    pass\n\nSALT_CHARS = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\nDEFAULT_PBKDF2_ITERATIONS = 600000\n\n_os_alt_seps: t.List[str] = list(\n    sep for sep in [os.sep, os.path.altsep] if sep is not None and sep != \"/\"\n)\n\n\ndef gen_salt(length: int) -> str:\n    \"\"\"Generate a random string of SALT_CHARS with specified ``length``.\"\"\"\n    if length <= 0:\n        raise ValueError(\"Salt length must be positive\")\n\n    return \"\".join(secrets.choice(SALT_CHARS) for _ in range(length))\n\n\ndef _hash_internal(method: str, salt: str, password: str) -> t.Tuple[str, str]:\n    \"\"\"Internal password hash helper.  Supports plaintext without salt,\n    unsalted and salted passwords.  In case salted passwords are used\n    hmac is used.\n    \"\"\"\n    if method == \"plain\":\n        return password, method\n\n    salt = salt.encode(\"utf-8\")\n    password = password.encode(\"utf-8\")\n\n    if method.startswith(\"pbkdf2:\"):\n        if not salt:\n            raise ValueError(\"Salt is required for PBKDF2\")\n\n        args = method[7:].split(\":\")\n\n        if len(args) not in (1, 2):\n            raise ValueError(\"Invalid number of arguments for PBKDF2\")\n\n        method = args.pop(0)\n        iterations = int(args[0] or 0) if args else DEFAULT_PBKDF2_ITERATIONS\n        return (\n            hashlib.pbkdf2_hmac(method, password, salt, iterations).hex(),\n            f\"pbkdf2:{method}:{iterations}\",\n        )\n\n    if salt:\n        return hmac.new(salt, password, method).hexdigest(), method\n\n    return hashlib.new(method, password).hexdigest(), method\n\n\ndef generate_password_hash(\n    password: str, method: str = \"pbkdf2:sha256\", salt_length: int = 16\n) -> str:\n    \"\"\"Hash a password with the given method and salt with a string of\n    the given length. The format of the string returned includes the method\n    that was used so that :func:`check_password_hash` can check the hash.\n\n    The format for the hashed string looks like this::\n\n        method$salt$hash\n\n    This method can **not** generate unsalted passwords but it is possible\n    to set param method='plain' in order to enforce plaintext passwords.\n    If a salt is used, hmac is used internally to salt the password.\n\n    If PBKDF2 is wanted it can be enabled by setting the method to\n    ``pbkdf2:method:iterations`` where iterations is optional::\n\n        pbkdf2:sha256:80000$salt$hash\n        pbkdf2:sha256$salt$hash\n\n    :param password: the password to hash.\n    :param method: the hash method to use (one that hashlib supports). Can\n                   optionally be in the format ``pbkdf2:method:iterations``\n                   to enable PBKDF2.\n    :param salt_length: the length of the salt in letters.\n    \"\"\"\n    salt = gen_salt(salt_length) if method != \"plain\" else \"\"\n    h, actual_method = _hash_internal(method, salt, password)\n    return f\"{actual_method}${salt}${h}\"\n\n\ndef check_password_hash(pwhash: str, password: str) -> bool:\n    \"\"\"Check a password against a given salted and hashed password value.\n    In order to support unsalted legacy passwords this method supports\n    plain text passwords, md5 and sha1 hashes (both salted and unsalted).\n\n    Returns `True` if the password matched, `False` otherwise.\n\n    :param pwhash: a hashed string like returned by\n                   :func:`generate_password_hash`.\n    :param password: the plaintext password to compare against the hash.\n    \"\"\"\n    if pwhash.count(\"$\") < 2:\n        return False\n\n    method, salt, hashval = pwhash.split(\"$\", 2)\n    return hmac.compare_digest(_hash_internal(method, salt, password)[0], hashval)\n\n\ndef safe_join(directory: str, *pathnames: str) -> t.Optional[str]:\n    \"\"\"Safely join zero or more untrusted path components to a base\n    directory to avoid escaping the base directory.\n\n    :param directory: The trusted base directory.\n    :param pathnames: The untrusted path components relative to the\n        base directory.\n    :return: A safe path, otherwise ``None``.\n    \"\"\"\n    if not directory:\n        # Ensure we end up with ./path if directory=\"\" is given,\n        # otherwise the first untrusted part could become trusted.\n        directory = \".\"\n\n    parts = [directory]\n\n    for filename in pathnames:\n        if filename != \"\":\n            filename = posixpath.normpath(filename)\n\n        if (\n            any(sep in filename for sep in _os_alt_seps)\n            or os.path.isabs(filename)\n            or filename == \"..\"\n            or filename.startswith(\"../\")\n        ):\n            return None\n\n        parts.append(filename)\n\n    return posixpath.join(*parts)\n", "path": "src/werkzeug/security.py"}]}