From 94962cef2eb306048b8ac7239c17192569e3f581 Mon Sep 17 00:00:00 2001 From: Michael Scovetta Date: Sat, 18 Mar 2017 23:01:47 -0700 Subject: [PATCH 1/5] Switch 'active' to 'disabled' for rules. --- DevSkim.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DevSkim.py b/DevSkim.py index fc3fd5d..93f7f42 100644 --- a/DevSkim.py +++ b/DevSkim.py @@ -637,8 +637,8 @@ class DevSkimEventListener(sublime_plugin.EventListener): logger.warning("Error suppressing rules for %s: %s" % (rule_id, msg)) - # Only include active rules -- if 'active' is not specified, assume True - rules = list(filter(lambda x: x.get('active', True), rules)) + # Only include non-disabled rules -- if 'disabled' is not specified, assume False + rules = list(filter(lambda x: not x.get('disabled', False), rules)) # Filter by tags, if specified, convert all to lowercase show_only_tags = set([k.lower().strip() From b0e5353c7d6e2912c5c34bff707e64aa74bbe454 Mon Sep 17 00:00:00 2001 From: Michael Scovetta Date: Sat, 18 Mar 2017 23:02:24 -0700 Subject: [PATCH 2/5] Fix web navigation, removing unnecessary error message. --- DevSkim.py | 1 + 1 file changed, 1 insertion(+) diff --git a/DevSkim.py b/DevSkim.py index 93f7f42..b3375d1 100644 --- a/DevSkim.py +++ b/DevSkim.py @@ -213,6 +213,7 @@ class DevSkimEventListener(sublime_plugin.EventListener): # Open a regular URL in the user's web browser if re.match("^https?://", command, re.IGNORECASE): webbrowser.open_new(command) + return # Special commands, intercept and perform the fix if command.startswith('#fixit'): From 401bb796ed42d1e7e1ff7f7df64effc598b90f7d Mon Sep 17 00:00:00 2001 From: Michael Scovetta Date: Sat, 18 Mar 2017 23:03:02 -0700 Subject: [PATCH 3/5] Add support for rule conditions. --- DevSkim.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/DevSkim.py b/DevSkim.py index b3375d1..bdcb1d8 100644 --- a/DevSkim.py +++ b/DevSkim.py @@ -844,7 +844,15 @@ class DevSkimEventListener(sublime_plugin.EventListener): if self.is_suppressed(rule, line_list): continue # Don't add the result to the list - result_list.append({ + # If there are conditions, run them now + context = { + 'filename': filename, + 'file_contents': file_contents, + 'rule': rule, + 'pattern': pattern_dict + } + + result_details = { 'rule': rule, 'match_content': match.group(), 'match_region': sublime.Region(start + offset, end + offset), @@ -852,13 +860,65 @@ class DevSkimEventListener(sublime_plugin.EventListener): 'match_end': end + offset, 'pattern': orig_pattern_str, 'scope_list': scope_list - }) + } + + if self.meets_conditions(context, result_details): + result_list.append(result_details) else: logger.debug("Not running rule check [force={0}, rule_applies={1}, syntax={2}, ext={3},]".format( force_analyze, rule_applies_to, set(rule_applies_to) & set(syntax_types), extension)) return result_list + def meets_conditions(self, context, result): + """Checks to see if a finding meets specified conditions from the rule.""" + logger.debug(context) + + pattern = context.get('pattern') + if not pattern: + return True # No pattern means same thing as them all passing. + + conditions = pattern.get('conditions') + if not conditions: + return True # No conditions means same as them all passing. + + match_start = result.get('match_start') + line = self.view.substr(self.view.line(match_start)) + + if not line: + logger.error('No line was found in meets_conditions') + return True # No line means something is broken + + def _line_match_all(line, targets): + logger.debug('_line_match_all({0}, {1})'.format(line, targets)) + + line = line.lower() + return all([t.lower() in line for t in targets]) + + def _line_match_any(line, targets): + line = line.lower() + return any([t.lower() in line for t in targets]) + + logger.debug('Found {0} conditions'.format(len(conditions))) + + result = True + + for condition in conditions: + name = condition.get('name') + value = condition.get('value') + invert = condition.get('invert', False) + + if name == 'line-match-all': + r = _line_match_all(line, value) + result &= not r if invert else r + elif name == 'line-match-any': + r = _line_match_any(line, value) + result &= not r if invert else r + else: + logger.warning('Invalid condition name: {0}'.format(name)) + + return result + def severity_abbreviation(self, severity): """Convert a severity name into an abbreviation.""" if severity is None: From e9326fd7758769f5dc5cdff8c457188161d55d6b Mon Sep 17 00:00:00 2001 From: Michael Scovetta Date: Sun, 19 Mar 2017 00:18:59 -0700 Subject: [PATCH 4/5] Improve error reporting (stack traces). --- DevSkim.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DevSkim.py b/DevSkim.py index bdcb1d8..7fbf7ed 100644 --- a/DevSkim.py +++ b/DevSkim.py @@ -342,6 +342,7 @@ class DevSkimEventListener(sublime_plugin.EventListener): self.analyze_current_view(view, show_popup=False, single_line=True) except Exception as msg: logger.warning("Error analyzing current view: %s", msg) + traceback.print_exc() def on_load_async(self, view): """Handle asynchronous loading event.""" @@ -353,6 +354,7 @@ class DevSkimEventListener(sublime_plugin.EventListener): self.analyze_current_view(view, show_popup=False) except Exception as msg: logger.warning("Error analyzing current view: %s", msg) + traceback.print_exc() def on_post_save_async(self, view): """Handle post-save events.""" @@ -366,6 +368,7 @@ class DevSkimEventListener(sublime_plugin.EventListener): self.analyze_current_view(view) except Exception as msg: logger.warning("Error analyzing current view: %s", msg) + traceback.print_exc() def analyze_current_view(self, view, show_popup=True, single_line=False): """Kick off the analysis.""" @@ -1048,6 +1051,7 @@ class DevSkimAnalyzeCommand(sublime_plugin.TextCommand): devskim_event_listener.analyze_current_view(self.view) except Exception as msg: logger.warning("Error analyzing current view: %s" % msg) + traceback.print_exc() class DevSkimReloadRulesCommand(sublime_plugin.TextCommand): From 1f669481800656e8bfa4314c99cdeecc52f6426b Mon Sep 17 00:00:00 2001 From: Michael Scovetta Date: Sun, 19 Mar 2017 00:19:27 -0700 Subject: [PATCH 5/5] Add support for .gitignore and ignore_files from settings. --- DevSkim.py | 47 ++++++++++++++++++++++++++++++++++++---- DevSkim.sublime-settings | 8 ++++++- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/DevSkim.py b/DevSkim.py index 7fbf7ed..62c3b5c 100644 --- a/DevSkim.py +++ b/DevSkim.py @@ -12,6 +12,8 @@ import logging import re import time import os +import shlex +import subprocess import traceback import webbrowser @@ -379,7 +381,19 @@ class DevSkimEventListener(sublime_plugin.EventListener): return window = view.window() - + + if not single_line: + self.clear_regions(view) + + # TRY + _v = window.extract_variables() + filename = _v.get('file', '').replace('\\', '/') + if filename: + if self.is_file_ignored(filename): + logger.info("File is ignored.") + return + # DONE + self.lazy_initialize() logger.debug("analyze_current_view()") @@ -1019,9 +1033,34 @@ class DevSkimEventListener(sublime_plugin.EventListener): max_width=max_width, max_height=max_height, on_navigate=on_navigate, - on_hide=on_hide), repeat_duration_ms) - + on_hide=on_hide), repeat_duration_ms) + + def is_file_ignored(self, filename): + """Check to see if a file (by filename) is ignored from analysis.""" + global user_settings + + if not filename: + return False + + if not user_settings.get('ignore_from_gitignore', True): + return False + for ignore_pattern in user_settings.get('ignore_files', []): + logger.warning("Checking {0}".format(ignore_pattern)) + if re.match(ignore_pattern, filename, re.IGNORECASE): + return True + + try: + cwd = os.path.dirname(filename) + filename = shlex.quote(filename) + output = subprocess.check_output("git check-ignore --no-index {0}; exit 0;".format(filename), + cwd=cwd, stderr=subprocess.STDOUT, shell=True) + return filename in output.decode('utf-8') + except Exception as msg: + logger.warning("Error checking if file is ignored: {0}".format(msg)) + + return False + class ReplaceTextCommand(sublime_plugin.TextCommand): """Simple function to route text changes to view.""" @@ -1063,10 +1102,10 @@ class DevSkimReloadRulesCommand(sublime_plugin.TextCommand): rules = [] stylesheet_content = "" - def plugin_loaded(): """Handle the plugin_loaded event from ST3.""" logger.info('DevSkim plugin_loaded(), Sublime Text v%s' % sublime.version()) + def plugin_unloaded(): diff --git a/DevSkim.sublime-settings b/DevSkim.sublime-settings index 831330c..60bf039 100644 --- a/DevSkim.sublime-settings +++ b/DevSkim.sublime-settings @@ -24,6 +24,13 @@ /* Sort results by either "severity" or "line_number" */ "sort_results_by": "line_number", + /* Ignore files specified in .gitignore */ + "ignore_from_gitignore": true, + + /* Ignore additional files, each is a regular expression, escaped for JSON */ + "ignore_files": [ + ], + /* Run analysis as soon as a file is opened. */ "show_highlights_on_load": true, @@ -52,5 +59,4 @@ /* Globally suppress these rules (list of rule IDs, like "DS123456") */ "suppress_rules": [ ] - }