-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
395 add the information about match context to the database #439
base: master
Are you sure you want to change the base?
395 add the information about match context to the database #439
Conversation
src/tasks.py
Outdated
"""Reads a specific range of bytes from the already loaded file content around a given offset. | ||
|
||
Args: | ||
data (bytes): Data to read. | ||
matched_length (int): Number of bytes to read. | ||
offset (int): The offset in bytes from which to start reading. | ||
byte_range (int): The range in bytes to read around the offset (default is 32). | ||
|
||
Returns: | ||
bytes: A chunk of bytes from the file, starting from the given offset minus bit_range | ||
and ending at offset plus matched_length and byte_range. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generated by chatgpt? 😀
I don't think the docstrings here are very good (some parts are actually plain wrong), and we don't generally use that docstring format for arguments, so I think argument docs can/should be removed. Maybe:
"""Reads a specific range of bytes from the already loaded file content around a given offset. | |
Args: | |
data (bytes): Data to read. | |
matched_length (int): Number of bytes to read. | |
offset (int): The offset in bytes from which to start reading. | |
byte_range (int): The range in bytes to read around the offset (default is 32). | |
Returns: | |
bytes: A chunk of bytes from the file, starting from the given offset minus bit_range | |
and ending at offset plus matched_length and byte_range. | |
"""Return `matched_length` bytes from `offset`, along with `byte_range` bytes before and after the match. |
src/tasks.py
Outdated
@staticmethod | ||
def read_file(file_path: str) -> bytes: | ||
"""Reads the entire file content. | ||
|
||
Returns: | ||
bytes: The content of the file. | ||
""" | ||
with open(file_path, "rb") as file: | ||
return file.read() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this method is necessary - you can just inline this.
Even if you prefer to keep it, it doesn't belong in this class (Agent). But IMO it's best to inline it.
src/tasks.py
Outdated
return file.read() | ||
|
||
@staticmethod | ||
def read_bytes_from_offset( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a very good method name. Maybe read_bytes_with_context
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also there's no reason to use @staticmethod
here - it's generic, and has no relation to the Agent class. Please make it a global method
src/tasks.py
Outdated
"after": base64.b64encode(after).decode("utf-8"), | ||
} | ||
) | ||
context.update({str(yara_match): match_context}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
str(yara_match)
? Probably better to avoid relying on str() representation and to use something like yara_match.rule
(or .name? I don't remember)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing that's missing is that you don't put string name anywhere - this makes it impossible to tell which string matched. In most cases there is a single yara rule per query, but multiple strings.
It definitely must be stored somewhere. Probably the easiest way is to use a dict for contetx instead of a list:
{
"rule_name_1": {
"string_1": {
"before": "CgkJCQkJcHJpbnQgU1RERVJSICRtc2c7CgkJCQkJdW4=",
"match": "AbcDe9f=",
"after": "CgkJCQkJcHJpbnQgU1RERVJSICRtc2c7CgkJCQkJdW4=",
},
"string_2": {
"before": "CgkJCQkJcHJpbnQgU1RERVJSICRtc2c7CgkJCQkJdW4=",
"match": "AbcDe9f=",
"after": "CgkJCQkJcHJpbnQgU1RERVJSICRtc2c7CgkJCQkJdW4=",
}
},
"rule_name_2": {
"string_1": {
"before": "CgkJCQkJcHJpbnQgU1RERVJSICRtc2c7CgkJCQkJdW4=",
"match": "AbcDe9f=",
"after": "CgkJCQkJcHJpbnQgU1RERVJSICRtc2c7CgkJCQkJdW4=",
},
"string_2": {
"before": "CgkJCQkJcHJpbnQgU1RERVJSICRtc2c7CgkJCQkJdW4=",
"match": "AbcDe9f=",
"after": "CgkJCQkJcHJpbnQgU1RERVJSICRtc2c7CgkJCQkJdW4=",
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finally, this expression is overly complicated - I believe
context.update({A: B})
is equivalent to
context[A] = B
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, in the match key, we have information about the string we are searching for. But okay, I’ll replace the list with a dictionry.
src/tasks.py
Outdated
for string_match in yara_match.strings: | ||
expression_keys = [] | ||
for expression_key in string_match.instances: | ||
if expression_key in expression_keys: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is a no-op - for (regular, with no eq override) python object object in SOMETHING
will always be false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't fully get the idea behind this. If the intention was to only add a single match to the context (as originally planned, I think) then you should check if string_match
was already added instead.
Or even better, just take the first instance - no loop necessary
src/tasks.py
Outdated
for yara_match in matches: | ||
match_context = [] | ||
for string_match in yara_match.strings: | ||
expression_keys = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is only used for presence checking, this should be a set instead of a list. But I think it's not necessary at all (see next comment)
src/tasks.py
Outdated
(before, matching, after,) = self.read_bytes_from_offset( | ||
data=data, | ||
offset=expression_key.offset, | ||
matched_length=expression_key.matched_length, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(before, matching, after,) = self.read_bytes_from_offset( | |
data=data, | |
offset=expression_key.offset, | |
matched_length=expression_key.matched_length, | |
) | |
(before, matching, after) = self.read_bytes_from_offset( | |
data, | |
expression_key.offset, | |
expression_key.matched_length, | |
) |
src/tasks.py
Outdated
@@ -140,6 +194,36 @@ def execute_yara(self, job: Job, files: List[str]) -> None: | |||
f"in {scanned_datasets}/{job.total_datasets} ({dataset_percent:.0%}) of datasets.", | |||
) | |||
|
|||
def get_match_context( | |||
self, data: bytes, matches: List[yara.Match] | |||
) -> dict: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for now we use typing classes everywhere (and also misses key/value types)
) -> dict: | |
) -> Dict[str, ???]: |
(value type to be fixed depending on how other comments are resolved)
src/tasks.py
Outdated
self.update_metadata( | ||
job.id, orig_name, path, [r.rule for r in matches] | ||
job=job.id, | ||
orig_name=orig_name, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why explicit parameter names everywhere? It's a bit verbose, but OK - just wondering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I read somewhere that when we use such a notation, we don’t have to worry about the order of the arguments, and we already know during the function call what variables are being assigned to (if the naming differs). That was someone’s opinion; I’ll adapt to the project then. :)
Your checklist for this pull request
What is the current behaviour?
What is the new behaviour?
Test plan
Closing issues
fixes #issuenumber