| Conditions | 21 |
| Total Lines | 80 |
| Code Lines | 66 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 0 | ||
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like sopel.modules.safety.url_handler() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | # coding=utf-8 |
||
| 116 | @sopel.module.rule(r'(?u).*(https?://\S+).*') |
||
| 117 | @sopel.module.priority('high') |
||
| 118 | def url_handler(bot, trigger): |
||
| 119 | """Checks for malicious URLs""" |
||
| 120 | check = True # Enable URL checking |
||
| 121 | strict = False # Strict mode: kick on malicious URL |
||
| 122 | positives = 0 # Number of engines saying it's malicious |
||
| 123 | total = 0 # Number of total engines |
||
| 124 | use_vt = True # Use VirusTotal |
||
| 125 | check = bot.config.safety.enabled_by_default |
||
| 126 | if check is None: |
||
| 127 | # If not set, assume default |
||
| 128 | check = True |
||
| 129 | # DB overrides config: |
||
| 130 | setting = bot.db.get_channel_value(trigger.sender, 'safety') |
||
| 131 | if setting is not None: |
||
| 132 | if setting == 'off': |
||
| 133 | return # Not checking |
||
| 134 | elif setting in ['on', 'strict', 'local', 'local strict']: |
||
| 135 | check = True |
||
| 136 | if setting == 'strict' or setting == 'local strict': |
||
| 137 | strict = True |
||
| 138 | if setting == 'local' or setting == 'local strict': |
||
| 139 | use_vt = False |
||
| 140 | |||
| 141 | if not check: |
||
| 142 | return # Not overridden by DB, configured default off |
||
| 143 | |||
| 144 | try: |
||
| 145 | netloc = urlparse(trigger.group(1)).netloc |
||
| 146 | except ValueError: |
||
| 147 | return # Invalid IPv6 URL |
||
| 148 | |||
| 149 | if any(regex.search(netloc) for regex in known_good): |
||
| 150 | return # Whitelisted |
||
| 151 | |||
| 152 | apikey = bot.config.safety.vt_api_key |
||
| 153 | try: |
||
| 154 | if apikey is not None and use_vt: |
||
| 155 | payload = {'resource': unicode(trigger), |
||
| 156 | 'apikey': apikey, |
||
| 157 | 'scan': '1'} |
||
| 158 | |||
| 159 | if trigger not in bot.memory['safety_cache']: |
||
| 160 | r = requests.post(vt_base_api_url + 'report', data=payload) |
||
| 161 | r.raise_for_status() |
||
| 162 | result = r.json() |
||
| 163 | age = time.time() |
||
| 164 | data = {'positives': result['positives'], |
||
| 165 | 'total': result['total'], |
||
| 166 | 'age': age} |
||
| 167 | bot.memory['safety_cache'][trigger] = data |
||
| 168 | if len(bot.memory['safety_cache']) > 1024: |
||
| 169 | _clean_cache(bot) |
||
| 170 | else: |
||
| 171 | print('using cache') |
||
| 172 | result = bot.memory['safety_cache'][trigger] |
||
| 173 | positives = result['positives'] |
||
| 174 | total = result['total'] |
||
| 175 | except requests.exceptions.RequestException: |
||
| 176 | LOGGER.debug('[VirusTotal] Error obtaining response.', exc_info=True) |
||
| 177 | pass # Ignoring exceptions with VT so MalwareDomains will always work |
||
| 178 | except InvalidJSONResponse: |
||
| 179 | LOGGER.debug('[VirusTotal] Malformed response (invalid JSON).', exc_info=True) |
||
| 180 | pass # Ignoring exceptions with VT so MalwareDomains will always work |
||
| 181 | |||
| 182 | if unicode(netloc).lower() in malware_domains: |
||
| 183 | # malwaredomains is more trustworthy than some VT engines |
||
| 184 | # therefore it gets a weight of 10 engines when calculating confidence |
||
| 185 | positives += 10 |
||
| 186 | total += 10 |
||
| 187 | |||
| 188 | if positives > 1: |
||
| 189 | # Possibly malicious URL detected! |
||
| 190 | confidence = '{}%'.format(round((positives / total) * 100)) |
||
| 191 | msg = 'link posted by %s is possibly malicious ' % bold(trigger.nick) |
||
| 192 | msg += '(confidence %s - %s/%s)' % (confidence, positives, total) |
||
| 193 | bot.say('[' + bold(color('WARNING', 'red')) + '] ' + msg) |
||
| 194 | if strict: |
||
| 195 | bot.kick(trigger.nick, trigger.sender, 'Posted a malicious link') |
||
| 196 | |||
| 229 |