-
Notifications
You must be signed in to change notification settings - Fork 364
Description
Proposal
I've said it a couple times already, and every time I have a look at that file I'm instantly lost. Thus, I'll propose to split this monstrosity in smaller chunks.
Generally, there are three things I want to address here:
One class per file
sessions.py is well over 1500 LoC. This warrants splitting the file into a subpackage; Import paths should not change. I'd consider this a low impact, non-breaking change.
Simplify the Constructor
Most of the constructor consists of making sure things are set, sanitizing them and/or providing defaults from the constants module. I think this could be made much cleaner by implementing these in a SessionOptions class where each valid option has a property that does sanitation and default-setting in a setter and have computed options in a (cached) getter.
As a stub, I'd propose something like this:
class SessionOptions:
def __init__(self, *args, **kwargs):
self.options = {}
for key, val in kwargs.items():
if hasattr(self, key):
setattr(self, key, val)
else:
# log invalid option supplied
@property
def web_address(self):
return self.options["web_address"] if "web_address" in self.options else ('127.0.0.1', 6300)
@property.setter
def web_address(self, value: tuple[str, int]):
self.options["web_address"] = valueand then remove most keyword arguments from session. This is a breaking change, and we should discuss how to handle this gracefully. One option would be to just pass all kwargs supplied to Session to the SessionOptions should session detect more than the expected number of kwargs, and then issue a FutureWarning. The more nuclear option would be to just remove them; after all, the code change in user code isn't too great.
This would also be the point where I'd remove the callbacks we deprecated in 2020; I think two years are enough and moving these to a new configuration system seems questionable to me.
An added benefit of such a SessionOption approach is that configuration can be easily loaded from a file. Some of the options, like web ports or whatever, might make sense to set project-independent in a boofuzzrc; but that's just a dream for later at this point.
Separate concerns
One reason why session is so large is that, in my opinion, it does too much. A prime example (and the lowest hanging fruit IMHO) is that the whole fuzzing loop is hard-coded into session. As one step towards a smaller session class I'd propose a rework of how the fuzzing loop works, abstracting it down to "for each test case, run through the steps in this ordered list".
This means that all current steps become classes following a common interface; these can be included or excluded at will. This also makes some more callbacks obsolete in favor of pluggable class steps that can be chained anywhere in the test case execution pipeline.
Obviously, this will be a huge shift in paradigm and probably introduce breaking changes. However, like I've done with the connections, it's possible to gracefully shim the existing API to use this pluggable chain instead. Additionally, many of the functions affected by such a rework are underscore function anyways, which I consider fair game to move without worrying about breaking someones code.
Use-Case
Use-Case: keep my sanity while developing, improve readability and maintainability.
Anything else?
What are your thoughts on this? I'll start a PR to implement all of the above and mark it as WIP. If you have comments about the architecture of how things should work, please share them. Most of this is only a high-level thought I have at the moment. Especially reworking the session options might not even be necessary to this extent once the test case pipeline is in place.