예제 #1
0
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
예제 #2
0
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)
예제 #3
0
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)
예제 #4
0
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
예제 #5
0
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)
예제 #6
0
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
예제 #7
0
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)
예제 #8
0
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)