def test_diagnostic_comparisons(): ''' Ensure Diagnostics can be properly compared ''' pos_dict = {"line": 10, "character": 15} pos = protocol.Position(line=pos_dict["line"], char=pos_dict["character"]) rg_dict = {"start": pos_dict, "end": pos_dict} rg_obj = protocol.Range(start=pos, end=pos) diag_dict = { "message": "Test Diagnostic", "range": rg_dict, "relatedInformation": [], "severity": protocol.DiagnosticSeverity.ERROR } diag = protocol.Diagnostic(locrange=rg_obj, message=diag_dict["message"], severity=diag_dict["severity"]) diag_same = deepcopy(diag) assert diag == diag_same diag_msg = deepcopy(diag) diag_msg.message = "Different Message Diagnostic" assert diag != diag_msg diag_sev = deepcopy(diag) diag_sev.severity = protocol.DiagnosticSeverity.WARNING assert diag != diag_sev diag_rinfo = deepcopy(diag) diag_rinfo.relatedInformation = ["New Information"] assert diag != diag_rinfo diag_rg = deepcopy(diag) new_pos = deepcopy(pos) new_pos.line = 0 new_pos.char = 0 diag_rg.range = protocol.Range(start=new_pos, end=new_pos) assert diag != diag_rg
def get_rule_range(document: str, pos: lsp.Position) -> lsp.Range: '''Get the range of the YARA rule that a given symbol is in :document: Text to search in To determine line numbers, text is split at newlines, and carriage returns are ignored :pos: Symbol position to base range off of ''' start_pattern = re.compile(r"^((private|global) )?rule\b") end_pattern = re.compile("^}$") lines = document.replace("\r", "").split("\n") # default to assuming the entire document is within range start_pos = lsp.Position(line=0, char=0) end_pos = lsp.Position(line=len(lines), char=0) # work backwards from the given position and find the start of rule for index in range(pos.line, 0, -1): line = lines[index] match = start_pattern.match(line) if match: start_pos = lsp.Position(line=index, char=0) break # start from the given position and find the first end of rule for index in range(pos.line, len(lines)): line = lines[index] match = end_pattern.match(line) if match: end_pos = lsp.Position(line=index, char=0) break return lsp.Range(start=start_pos, end=end_pos)
def test_range(): ''' Ensure Ranges is properly encoded to JSON dictionaries ''' pos_dict = {"line": 10, "character": 15} pos = protocol.Position(line=pos_dict["line"], char=pos_dict["character"]) rg_dict = {"start": pos_dict, "end": pos_dict} rg_obj = protocol.Range(start=pos, end=pos) assert json.dumps(rg_obj, cls=protocol.JSONEncoder) == json.dumps(rg_dict)
def test_location_comparisons(): ''' Ensure Locations can be properly compared ''' pos = protocol.Position(line=10, char=15) rg_obj = protocol.Range(start=pos, end=pos) loc = protocol.Location(locrange=rg_obj, uri="fake:///one/two/three/four.path") loc_same = deepcopy(loc) assert loc == loc_same loc_range = deepcopy(loc) new_pos = deepcopy(pos) new_pos.line = 0 new_range = protocol.Range(start=new_pos, end=new_pos) loc_range.range = new_range assert loc != loc_range loc_uri = deepcopy(loc) loc_uri.uri = "fake:///five/six/seven/eight.path" assert loc != loc_uri
def test_location(): ''' Ensure Location is properly encoded to JSON dictionaries ''' pos_dict = {"line": 10, "character": 15} pos = protocol.Position(line=pos_dict["line"], char=pos_dict["character"]) rg_dict = {"start": pos_dict, "end": pos_dict} rg_obj = protocol.Range(start=pos, end=pos) loc_dict = {"range": rg_dict, "uri": "fake:///one/two/three/four.path"} loc = protocol.Location(locrange=rg_obj, uri=loc_dict["uri"]) assert json.dumps(loc, cls=protocol.JSONEncoder) == json.dumps(loc_dict)
def test_range_comparisons(): ''' Ensure Range can be properly compared ''' pos = protocol.Position(line=10, char=15) rg_obj = protocol.Range(start=pos, end=pos) rg_same = deepcopy(rg_obj) assert rg_obj == rg_same rg_start = deepcopy(rg_obj) new_end = deepcopy(pos) new_end.line = 0 rg_start.start = new_end assert rg_obj != rg_start rg_end = deepcopy(rg_obj) new_end = deepcopy(pos) new_end.line = 0 rg_end.end = new_end assert rg_obj != rg_end
def test_diagnostic(): ''' Ensure Diagnostic is properly encoded to JSON dictionaries ''' pos_dict = {"line": 10, "character": 15} pos = protocol.Position(line=pos_dict["line"], char=pos_dict["character"]) rg_dict = {"start": pos_dict, "end": pos_dict} rg_obj = protocol.Range(start=pos, end=pos) diag_dict = { "message": "Test Diagnostic", "range": rg_dict, "relatedInformation": [], "severity": 1 } diag = protocol.Diagnostic(locrange=rg_obj, message=diag_dict["message"], severity=diag_dict["severity"]) assert json.dumps(diag, cls=protocol.JSONEncoder) == json.dumps(diag_dict)
async def test__compile_all_rules_with_dirty_files(test_rules, yara_server): ''' Ensure the _compile_all_rules function returns the appropriate number of diagnostics when workspace files have unsaved content ''' # TODO: figure out why YARA emits different messages on Windows and non-Windows platforms peek_rules_diagnostic = protocol.Diagnostic( protocol.Range(protocol.Position(line=17, char=8), protocol.Position(line=17, char=yara_server.MAX_LINE)), severity=protocol.DiagnosticSeverity.ERROR, message="syntax error, unexpected <true>, expecting text string") if sys.platform == "win32": peek_rules_diagnostic = protocol.Diagnostic( protocol.Range( protocol.Position(line=42, char=8), protocol.Position(line=42, char=yara_server.MAX_LINE)), severity=protocol.DiagnosticSeverity.ERROR, message="undefined string \"$hex_string\"") expected = [{ "uri": helpers.create_file_uri( str(test_rules.joinpath("peek_rules.yara").resolve())), "diagnostics": [peek_rules_diagnostic] }, { "uri": helpers.create_file_uri( str(test_rules.joinpath("code_completion.yara").resolve())), "diagnostics": [ protocol.Diagnostic(protocol.Range( protocol.Position(line=27, char=0), protocol.Position(line=27, char=yara_server.MAX_LINE)), severity=protocol.DiagnosticSeverity.ERROR, message="wrong usage of identifier \"is_dll\"") ] }, { "uri": helpers.create_file_uri( str(test_rules.joinpath("simple_mistake.yar").resolve())), "diagnostics": [ protocol.Diagnostic(protocol.Range( protocol.Position(line=4, char=0), protocol.Position(line=4, char=yara_server.MAX_LINE)), severity=protocol.DiagnosticSeverity.ERROR, message="undefined string \"$true\"") ] }] # py3.8 + py3.9 on Windows also add the following diagnostic # may be related to https://github.com/VirusTotal/yara-python/issues/150 if sys.platform == "win32" and sys.version_info >= (3, 8): expected.append({ "uri": helpers.create_file_uri( str(test_rules.joinpath("formatting.yar").resolve())), "diagnostics": [ protocol.Diagnostic( protocol.Range( protocol.Position(line=14, char=8), protocol.Position(line=14, char=yara_server.MAX_LINE)), severity=protocol.DiagnosticSeverity.ERROR, message="invalid field name \"number_of_signatures\"") ] }) # files won't actually be changed, so the diagnostics should reflect the "no_dirty_files" test dirty_files = {} for filename in [ "peek_rules.yara", "simple_mistake.yar", "code_completion.yara" ]: dirty_path = str(test_rules.joinpath(filename).resolve()) dirty_files[helpers.create_file_uri( dirty_path)] = yara_server._get_document(dirty_path, dirty_files={}) results = await yara_server._compile_all_rules(dirty_files, workspace=test_rules) assert len(results) == len( expected ), "Mismatched number of results. Got {:d} but expected {:d}".format( len(results), len(expected)) print(json.dumps(results, cls=protocol.JSONEncoder)) assert all(result in expected for result in results)