We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
adding root:x:!password to userdb.txt doesn't exclude root/password as valid credentials
Fresh install.

I tried to exclude 'password' or 'abc123' from valid passwords for the user root

Now file looks like 
```
root:x:!root
root:x:!123456
root:x:!password
root:x:*
```

Retarted cowrie, but no way to deny login with root/password credentials

Maybe, some sort of problem with new regexp checking?

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `cowrie/core/auth.py`
Content:
```
1 # Copyright (c) 2009-2014 Upi Tamminen <desaster@gmail.com>
2 # See the COPYRIGHT file for more information
3 
4 """
5 This module contains ...
6 """
7 
8 from __future__ import division, absolute_import
9 
10 import re
11 import json
12 from os import path
13 from random import randint
14 
15 from twisted.python import log
16 
17 from cowrie.core.config import CONFIG
18 
19 class UserDB(object):
20     """
21     By Walter de Jong <walter@sara.nl>
22     """
23 
24     def __init__(self):
25         self.userdb = {}
26         self.userdb_file = '{}/userdb.txt'.format(CONFIG.get('honeypot', 'data_path'))
27         self.load()
28 
29 
30     def load(self):
31         """
32         load the user db
33         """
34 
35         with open(self.userdb_file, 'rb') as f:
36             while True:
37                 rawline = f.readline()
38                 if not rawline:
39                     break
40 
41                 line = rawline.strip()
42                 if not line:
43                     continue
44 
45                 if line.startswith(b'#'):
46                     continue
47 
48                 login, passwd = re.split(br':\w+:', line, 1)
49                 self.adduser(login, passwd)
50 
51 
52     def checklogin(self, thelogin, thepasswd, src_ip='0.0.0.0'):
53         for credentials, policy in self.userdb.items():
54             login, passwd = credentials
55 
56             if self.match_rule(login, thelogin):
57                 if self.match_rule(passwd, thepasswd):
58                     return policy
59 
60         return False
61 
62 
63     def match_rule(self, rule, input):
64         if type(rule) is bytes:
65             return rule in [b'*', input]
66         else:
67             return bool(rule.search(input))
68 
69 
70     def re_or_str(self, rule):
71         """
72         Convert a /.../ type rule to a regex, otherwise return the string as-is
73         """
74         res = re.match(br'/(.+)/(i)?$', rule)
75         if res:
76             return re.compile(res.group(1), re.IGNORECASE if res.group(2) else 0)
77 
78         return rule
79 
80 
81     def adduser(self, login, passwd):
82         login = self.re_or_str(login)
83 
84         if passwd.startswith(b'!'):
85             policy = False
86             passwd = passwd[1:]
87         else:
88             policy = True
89 
90         passwd = self.re_or_str(passwd)
91         self.userdb[(login, passwd)] = policy
92 
93 
94 class AuthRandom(object):
95     """
96     Alternative class that defines the checklogin() method.
97     Users will be authenticated after a random number of attempts.
98     """
99 
100     def __init__(self):
101         # Default values
102         self.mintry, self.maxtry, self.maxcache = 2, 5, 10
103 
104         # Are there auth_class parameters?
105         if CONFIG.has_option('honeypot', 'auth_class_parameters'):
106             parameters = CONFIG.get('honeypot', 'auth_class_parameters')
107             parlist = parameters.split(',')
108             if len(parlist) == 3:
109                 self.mintry = int(parlist[0])
110                 self.maxtry = int(parlist[1])
111                 self.maxcache = int(parlist[2])
112 
113         if self.maxtry < self.mintry:
114             self.maxtry = self.mintry + 1
115             log.msg("maxtry < mintry, adjusting maxtry to: {}".format(self.maxtry))
116         self.uservar = {}
117         self.uservar_file = '{}/auth_random.json'.format(CONFIG.get('honeypot', 'state_path'))
118         self.loadvars()
119 
120 
121     def loadvars(self):
122         """
123         Load user vars from json file
124         """
125         if path.isfile(self.uservar_file):
126             with open(self.uservar_file, 'rb') as fp:
127                 try:
128                     self.uservar = json.load(fp)
129                 except:
130                     self.uservar = {}
131 
132 
133     def savevars(self):
134         """
135         Save the user vars to json file
136         """
137         data = self.uservar
138         # Note: this is subject to races between cowrie logins
139         with open(self.uservar_file, 'wb') as fp:
140             json.dump(data, fp)
141 
142 
143     def checklogin(self, thelogin, thepasswd, src_ip):
144         """
145         Every new source IP will have to try a random number of times between
146         'mintry' and 'maxtry' before succeeding to login.
147         All username/password combinations  must be different.
148         The successful login combination is stored with the IP address.
149         Successful username/passwords pairs are also cached for 'maxcache' times.
150         This is to allow access for returns from different IP addresses.
151         Variables are saved in 'uservar.json' in the data directory.
152         """
153 
154         auth = False
155         userpass = thelogin + ':' + thepasswd
156 
157         if not 'cache' in self.uservar:
158             self.uservar['cache'] = []
159         cache = self.uservar['cache']
160 
161         # Check if it is the first visit from src_ip
162         if src_ip not in self.uservar:
163             self.uservar[src_ip] = {}
164             ipinfo = self.uservar[src_ip]
165             ipinfo['try'] = 0
166             if userpass in cache:
167                 log.msg("first time for {}, found cached: {}".format(src_ip, userpass))
168                 ipinfo['max'] = 1
169                 ipinfo['user'] = thelogin
170                 ipinfo['pw'] = thepasswd
171                 auth = True
172                 self.savevars()
173                 return auth
174             else:
175                 ipinfo['max'] = randint(self.mintry, self.maxtry)
176                 log.msg("first time for {}, need: {}".format(src_ip, ipinfo['max']))
177 
178         ipinfo = self.uservar[src_ip]
179 
180         # Fill in missing variables
181         if not 'max' in ipinfo:
182             ipinfo['max'] = randint(self.mintry, self.maxtry)
183         if not 'try' in ipinfo:
184             ipinfo['try'] = 0
185         if not 'tried' in ipinfo:
186             ipinfo['tried'] = []
187 
188         # Don't count repeated username/password combinations
189         if userpass in ipinfo['tried']:
190             log.msg('already tried this combination')
191             self.savevars()
192             return auth
193 
194         ipinfo['try'] += 1
195         attempts = ipinfo['try']
196         need = ipinfo['max']
197         log.msg("login attempt: {}".format(attempts))
198 
199         # Check if enough login attempts are tried
200         if attempts < need:
201             self.uservar[src_ip]['tried'].append(userpass)
202         elif attempts == need:
203             ipinfo['user'] = thelogin
204             ipinfo['pw'] = thepasswd
205             cache.append(userpass)
206             if len(cache) > self.maxcache:
207                 cache.pop(0)
208             auth = True
209         # Returning after successful login
210         elif attempts > need:
211             if not 'user' in ipinfo or not 'pw' in ipinfo:
212                 log.msg('return, but username or password not set!!!')
213                 ipinfo['tried'].append(userpass)
214                 ipinfo['try'] = 1
215             else:
216                 log.msg("login return, expect: [{}/{}]".format(ipinfo['user'], ipinfo['pw']))
217                 if thelogin == ipinfo['user'] and thepasswd == ipinfo['pw']:
218                     auth = True
219         self.savevars()
220         return auth
221 
222
```
--- 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()

```

diff --git a/cowrie/core/auth.py b/cowrie/core/auth.py
--- a/cowrie/core/auth.py
+++ b/cowrie/core/auth.py
@@ -11,6 +11,7 @@
 import json
 from os import path
 from random import randint
+from collections import OrderedDict
 
 from twisted.python import log
 
@@ -22,7 +23,7 @@
     """
 
     def __init__(self):
-        self.userdb = {}
+        self.userdb = OrderedDict()
         self.userdb_file = '{}/userdb.txt'.format(CONFIG.get('honeypot', 'data_path'))
         self.load()

{"golden_diff": "diff --git a/cowrie/core/auth.py b/cowrie/core/auth.py\n--- a/cowrie/core/auth.py\n+++ b/cowrie/core/auth.py\n@@ -11,6 +11,7 @@\n import json\n from os import path\n from random import randint\n+from collections import OrderedDict\n \n from twisted.python import log\n \n@@ -22,7 +23,7 @@\n     \"\"\"\n \n     def __init__(self):\n-        self.userdb = {}\n+        self.userdb = OrderedDict()\n         self.userdb_file = '{}/userdb.txt'.format(CONFIG.get('honeypot', 'data_path'))\n         self.load()\n", "issue": "adding root:x:!password to userdb.txt doesn't exclude root/password as valid credentials\nFresh install.\r\n\r\nI tried to exclude 'password' or 'abc123' from valid passwords for the user root\r\n\r\nNow file looks like \r\n```\r\nroot:x:!root\r\nroot:x:!123456\r\nroot:x:!password\r\nroot:x:*\r\n```\r\n\r\nRetarted cowrie, but no way to deny login with root/password credentials\r\n\r\nMaybe, some sort of problem with new regexp checking?\n", "before_files": [{"content": "# Copyright (c) 2009-2014 Upi Tamminen <desaster@gmail.com>\n# See the COPYRIGHT file for more information\n\n\"\"\"\nThis module contains ...\n\"\"\"\n\nfrom __future__ import division, absolute_import\n\nimport re\nimport json\nfrom os import path\nfrom random import randint\n\nfrom twisted.python import log\n\nfrom cowrie.core.config import CONFIG\n\nclass UserDB(object):\n    \"\"\"\n    By Walter de Jong <walter@sara.nl>\n    \"\"\"\n\n    def __init__(self):\n        self.userdb = {}\n        self.userdb_file = '{}/userdb.txt'.format(CONFIG.get('honeypot', 'data_path'))\n        self.load()\n\n\n    def load(self):\n        \"\"\"\n        load the user db\n        \"\"\"\n\n        with open(self.userdb_file, 'rb') as f:\n            while True:\n                rawline = f.readline()\n                if not rawline:\n                    break\n\n                line = rawline.strip()\n                if not line:\n                    continue\n\n                if line.startswith(b'#'):\n                    continue\n\n                login, passwd = re.split(br':\\w+:', line, 1)\n                self.adduser(login, passwd)\n\n\n    def checklogin(self, thelogin, thepasswd, src_ip='0.0.0.0'):\n        for credentials, policy in self.userdb.items():\n            login, passwd = credentials\n\n            if self.match_rule(login, thelogin):\n                if self.match_rule(passwd, thepasswd):\n                    return policy\n\n        return False\n\n\n    def match_rule(self, rule, input):\n        if type(rule) is bytes:\n            return rule in [b'*', input]\n        else:\n            return bool(rule.search(input))\n\n\n    def re_or_str(self, rule):\n        \"\"\"\n        Convert a /.../ type rule to a regex, otherwise return the string as-is\n        \"\"\"\n        res = re.match(br'/(.+)/(i)?$', rule)\n        if res:\n            return re.compile(res.group(1), re.IGNORECASE if res.group(2) else 0)\n\n        return rule\n\n\n    def adduser(self, login, passwd):\n        login = self.re_or_str(login)\n\n        if passwd.startswith(b'!'):\n            policy = False\n            passwd = passwd[1:]\n        else:\n            policy = True\n\n        passwd = self.re_or_str(passwd)\n        self.userdb[(login, passwd)] = policy\n\n\nclass AuthRandom(object):\n    \"\"\"\n    Alternative class that defines the checklogin() method.\n    Users will be authenticated after a random number of attempts.\n    \"\"\"\n\n    def __init__(self):\n        # Default values\n        self.mintry, self.maxtry, self.maxcache = 2, 5, 10\n\n        # Are there auth_class parameters?\n        if CONFIG.has_option('honeypot', 'auth_class_parameters'):\n            parameters = CONFIG.get('honeypot', 'auth_class_parameters')\n            parlist = parameters.split(',')\n            if len(parlist) == 3:\n                self.mintry = int(parlist[0])\n                self.maxtry = int(parlist[1])\n                self.maxcache = int(parlist[2])\n\n        if self.maxtry < self.mintry:\n            self.maxtry = self.mintry + 1\n            log.msg(\"maxtry < mintry, adjusting maxtry to: {}\".format(self.maxtry))\n        self.uservar = {}\n        self.uservar_file = '{}/auth_random.json'.format(CONFIG.get('honeypot', 'state_path'))\n        self.loadvars()\n\n\n    def loadvars(self):\n        \"\"\"\n        Load user vars from json file\n        \"\"\"\n        if path.isfile(self.uservar_file):\n            with open(self.uservar_file, 'rb') as fp:\n                try:\n                    self.uservar = json.load(fp)\n                except:\n                    self.uservar = {}\n\n\n    def savevars(self):\n        \"\"\"\n        Save the user vars to json file\n        \"\"\"\n        data = self.uservar\n        # Note: this is subject to races between cowrie logins\n        with open(self.uservar_file, 'wb') as fp:\n            json.dump(data, fp)\n\n\n    def checklogin(self, thelogin, thepasswd, src_ip):\n        \"\"\"\n        Every new source IP will have to try a random number of times between\n        'mintry' and 'maxtry' before succeeding to login.\n        All username/password combinations  must be different.\n        The successful login combination is stored with the IP address.\n        Successful username/passwords pairs are also cached for 'maxcache' times.\n        This is to allow access for returns from different IP addresses.\n        Variables are saved in 'uservar.json' in the data directory.\n        \"\"\"\n\n        auth = False\n        userpass = thelogin + ':' + thepasswd\n\n        if not 'cache' in self.uservar:\n            self.uservar['cache'] = []\n        cache = self.uservar['cache']\n\n        # Check if it is the first visit from src_ip\n        if src_ip not in self.uservar:\n            self.uservar[src_ip] = {}\n            ipinfo = self.uservar[src_ip]\n            ipinfo['try'] = 0\n            if userpass in cache:\n                log.msg(\"first time for {}, found cached: {}\".format(src_ip, userpass))\n                ipinfo['max'] = 1\n                ipinfo['user'] = thelogin\n                ipinfo['pw'] = thepasswd\n                auth = True\n                self.savevars()\n                return auth\n            else:\n                ipinfo['max'] = randint(self.mintry, self.maxtry)\n                log.msg(\"first time for {}, need: {}\".format(src_ip, ipinfo['max']))\n\n        ipinfo = self.uservar[src_ip]\n\n        # Fill in missing variables\n        if not 'max' in ipinfo:\n            ipinfo['max'] = randint(self.mintry, self.maxtry)\n        if not 'try' in ipinfo:\n            ipinfo['try'] = 0\n        if not 'tried' in ipinfo:\n            ipinfo['tried'] = []\n\n        # Don't count repeated username/password combinations\n        if userpass in ipinfo['tried']:\n            log.msg('already tried this combination')\n            self.savevars()\n            return auth\n\n        ipinfo['try'] += 1\n        attempts = ipinfo['try']\n        need = ipinfo['max']\n        log.msg(\"login attempt: {}\".format(attempts))\n\n        # Check if enough login attempts are tried\n        if attempts < need:\n            self.uservar[src_ip]['tried'].append(userpass)\n        elif attempts == need:\n            ipinfo['user'] = thelogin\n            ipinfo['pw'] = thepasswd\n            cache.append(userpass)\n            if len(cache) > self.maxcache:\n                cache.pop(0)\n            auth = True\n        # Returning after successful login\n        elif attempts > need:\n            if not 'user' in ipinfo or not 'pw' in ipinfo:\n                log.msg('return, but username or password not set!!!')\n                ipinfo['tried'].append(userpass)\n                ipinfo['try'] = 1\n            else:\n                log.msg(\"login return, expect: [{}/{}]\".format(ipinfo['user'], ipinfo['pw']))\n                if thelogin == ipinfo['user'] and thepasswd == ipinfo['pw']:\n                    auth = True\n        self.savevars()\n        return auth\n\n", "path": "cowrie/core/auth.py"}], "after_files": [{"content": "# Copyright (c) 2009-2014 Upi Tamminen <desaster@gmail.com>\n# See the COPYRIGHT file for more information\n\n\"\"\"\nThis module contains ...\n\"\"\"\n\nfrom __future__ import division, absolute_import\n\nimport re\nimport json\nfrom os import path\nfrom random import randint\nfrom collections import OrderedDict\n\nfrom twisted.python import log\n\nfrom cowrie.core.config import CONFIG\n\nclass UserDB(object):\n    \"\"\"\n    By Walter de Jong <walter@sara.nl>\n    \"\"\"\n\n    def __init__(self):\n        self.userdb = OrderedDict()\n        self.userdb_file = '{}/userdb.txt'.format(CONFIG.get('honeypot', 'data_path'))\n        self.load()\n\n\n    def load(self):\n        \"\"\"\n        load the user db\n        \"\"\"\n\n        with open(self.userdb_file, 'rb') as f:\n            while True:\n                rawline = f.readline()\n                if not rawline:\n                    break\n\n                line = rawline.strip()\n                if not line:\n                    continue\n\n                if line.startswith(b'#'):\n                    continue\n\n                login, passwd = re.split(br':\\w+:', line, 1)\n                self.adduser(login, passwd)\n\n\n    def checklogin(self, thelogin, thepasswd, src_ip='0.0.0.0'):\n        for credentials, policy in self.userdb.items():\n            login, passwd = credentials\n\n            if self.match_rule(login, thelogin):\n                if self.match_rule(passwd, thepasswd):\n                    return policy\n\n        return False\n\n\n    def match_rule(self, rule, input):\n        if type(rule) is bytes:\n            return rule in [b'*', input]\n        else:\n            return bool(rule.search(input))\n\n\n    def re_or_str(self, rule):\n        \"\"\"\n        Convert a /.../ type rule to a regex, otherwise return the string as-is\n        \"\"\"\n        res = re.match(br'/(.+)/(i)?$', rule)\n        if res:\n            return re.compile(res.group(1), re.IGNORECASE if res.group(2) else 0)\n\n        return rule\n\n\n    def adduser(self, login, passwd):\n        login = self.re_or_str(login)\n\n        if passwd.startswith(b'!'):\n            policy = False\n            passwd = passwd[1:]\n        else:\n            policy = True\n\n        passwd = self.re_or_str(passwd)\n        self.userdb[(login, passwd)] = policy\n\n\nclass AuthRandom(object):\n    \"\"\"\n    Alternative class that defines the checklogin() method.\n    Users will be authenticated after a random number of attempts.\n    \"\"\"\n\n    def __init__(self):\n        # Default values\n        self.mintry, self.maxtry, self.maxcache = 2, 5, 10\n\n        # Are there auth_class parameters?\n        if CONFIG.has_option('honeypot', 'auth_class_parameters'):\n            parameters = CONFIG.get('honeypot', 'auth_class_parameters')\n            parlist = parameters.split(',')\n            if len(parlist) == 3:\n                self.mintry = int(parlist[0])\n                self.maxtry = int(parlist[1])\n                self.maxcache = int(parlist[2])\n\n        if self.maxtry < self.mintry:\n            self.maxtry = self.mintry + 1\n            log.msg(\"maxtry < mintry, adjusting maxtry to: {}\".format(self.maxtry))\n        self.uservar = {}\n        self.uservar_file = '{}/auth_random.json'.format(CONFIG.get('honeypot', 'state_path'))\n        self.loadvars()\n\n\n    def loadvars(self):\n        \"\"\"\n        Load user vars from json file\n        \"\"\"\n        if path.isfile(self.uservar_file):\n            with open(self.uservar_file, 'rb') as fp:\n                try:\n                    self.uservar = json.load(fp)\n                except:\n                    self.uservar = {}\n\n\n    def savevars(self):\n        \"\"\"\n        Save the user vars to json file\n        \"\"\"\n        data = self.uservar\n        # Note: this is subject to races between cowrie logins\n        with open(self.uservar_file, 'wb') as fp:\n            json.dump(data, fp)\n\n\n    def checklogin(self, thelogin, thepasswd, src_ip):\n        \"\"\"\n        Every new source IP will have to try a random number of times between\n        'mintry' and 'maxtry' before succeeding to login.\n        All username/password combinations  must be different.\n        The successful login combination is stored with the IP address.\n        Successful username/passwords pairs are also cached for 'maxcache' times.\n        This is to allow access for returns from different IP addresses.\n        Variables are saved in 'uservar.json' in the data directory.\n        \"\"\"\n\n        auth = False\n        userpass = thelogin + ':' + thepasswd\n\n        if not 'cache' in self.uservar:\n            self.uservar['cache'] = []\n        cache = self.uservar['cache']\n\n        # Check if it is the first visit from src_ip\n        if src_ip not in self.uservar:\n            self.uservar[src_ip] = {}\n            ipinfo = self.uservar[src_ip]\n            ipinfo['try'] = 0\n            if userpass in cache:\n                log.msg(\"first time for {}, found cached: {}\".format(src_ip, userpass))\n                ipinfo['max'] = 1\n                ipinfo['user'] = thelogin\n                ipinfo['pw'] = thepasswd\n                auth = True\n                self.savevars()\n                return auth\n            else:\n                ipinfo['max'] = randint(self.mintry, self.maxtry)\n                log.msg(\"first time for {}, need: {}\".format(src_ip, ipinfo['max']))\n\n        ipinfo = self.uservar[src_ip]\n\n        # Fill in missing variables\n        if not 'max' in ipinfo:\n            ipinfo['max'] = randint(self.mintry, self.maxtry)\n        if not 'try' in ipinfo:\n            ipinfo['try'] = 0\n        if not 'tried' in ipinfo:\n            ipinfo['tried'] = []\n\n        # Don't count repeated username/password combinations\n        if userpass in ipinfo['tried']:\n            log.msg('already tried this combination')\n            self.savevars()\n            return auth\n\n        ipinfo['try'] += 1\n        attempts = ipinfo['try']\n        need = ipinfo['max']\n        log.msg(\"login attempt: {}\".format(attempts))\n\n        # Check if enough login attempts are tried\n        if attempts < need:\n            self.uservar[src_ip]['tried'].append(userpass)\n        elif attempts == need:\n            ipinfo['user'] = thelogin\n            ipinfo['pw'] = thepasswd\n            cache.append(userpass)\n            if len(cache) > self.maxcache:\n                cache.pop(0)\n            auth = True\n        # Returning after successful login\n        elif attempts > need:\n            if not 'user' in ipinfo or not 'pw' in ipinfo:\n                log.msg('return, but username or password not set!!!')\n                ipinfo['tried'].append(userpass)\n                ipinfo['try'] = 1\n            else:\n                log.msg(\"login return, expect: [{}/{}]\".format(ipinfo['user'], ipinfo['pw']))\n                if thelogin == ipinfo['user'] and thepasswd == ipinfo['pw']:\n                    auth = True\n        self.savevars()\n        return auth\n\n", "path": "cowrie/core/auth.py"}]}