forked from nil0x42/phpsploit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
metadict.py
174 lines (145 loc) · 6.36 KB
/
metadict.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
"""Advanced dict-like classes for processing phpsploit
complex session objects.
"""
__all__ = ["MetaDict", "VarContainer"]
from ui.color import colorize
from utils.regex import WORD_TOKEN
class MetaDict(dict):
"""MetaDict() object
An advanced dict() like class with advanced features,
for phpsploit session objects.
Metadict() can be instanciated from a built-in dict().
Otherwise, it instanciates itself as en empty directory.
Magic item access
-----------------
The _isattr() method is used as pattern syntax validator on
__getattribute__() and __setattr__() methods in order to
respectively use __getitem__() and __setitem__() magic methods
instead.
Defaultly, the _isattr() function returns False anyway. This
means that this nice feature is disabled until overwritten
by some child class.
Example:
An hypothetical child class may enable the item access feature
explained above by rewritting the _isattr() method like this:
>>> def _isattr(self, name):
... return name == name.capitalize()
This way, any called instance attribute whose name is in
capitalized format will be automagically linked to the self
dict's corresponding item name, like in the folowing
concrete case:
>>> obj = MetaDict({'Foo':'bar', 'baz':'qux'})
# items whose name _isattr() can be accessed as attributes:
>>> assert obj["Foo"]` == obj.Foo
# in this example, 'baz' doesn't match _isattr() (capitalize())
>>> assert obj["baz"] != obj.baz
Dynamic block display
---------------------
This base class provides a nice dynamic columnized multiline
string representation of the self dict items. This behavior
is managed by the __call__() magic method.
The optionnal argument `pattern` can be used to limit
item display to keys whose name starts with it's value.
When displayed, the output string uses self __doc__'s first
line as title unless alternative title had been provided as
__init__() named optionnal argument.
If the self dict does not contains any item, or the pattern
filter does not match any item name, a ValueError is raised.
NOTE: Calling the magic __str__() method returns the __call__()
string result (without `pattern` argument.
"""
# pylint: disable=super-init-not-called
def __init__(self, value=None, title=None):
if value is None:
value = {}
self.update(value)
if title is None:
if self.__doc__:
self.title = self.__doc__.splitlines()[0].strip()
else:
self.title = "%s() object" % self.__class__.__name__
else:
self.title = str(title)
def __getattribute__(self, name):
if name != "_isattr" and self._isattr(name):
return self.__getitem__(name)
return super().__getattribute__(name)
def __setattr__(self, name, value):
if self._isattr(name):
return self.__setitem__(name, value)
return super().__setattr__(name, value)
def __dir__(self):
"""considering the special get/set behavior, __dir__() method
shall also return self item whose name matches self._isattr().
"""
return super().__dir__() + [i for i in self.keys() if self._isattr(i)]
def _isattr(self, name): # pylint: disable=unused-argument,no-self-use
"""Determine whether a called attribute name may be
considered as an item call. By default, it returns False
anyway, disabling that feature.
"""
return False
def __str__(self):
"""Return self __call__() method"""
return self.__call__()
def __call__(self, pattern=""):
"""Display self dict's items as a formatted multiline string array.
The optionnal argument `pattern` is an optional prefix to only
display matching items.
"""
# get matching vars list
sing_title = self.title
if sing_title.endswith("s"):
sing_title = sing_title[:-1]
if not self.keys():
raise ValueError("No such " + sing_title)
keys = [k for k in self.keys() if k.startswith(pattern)]
if not keys:
msg = "No {} matching «{}»"
raise ValueError(msg.format(sing_title, pattern))
tpl = (" {:%s} {}\n") % max(8, len(max(keys, key=len)))
buffer = self.title + "\n" + ("=" * len(self.title)) + "\n\n"
buffer += tpl.format("Variable", "Value")
buffer += tpl.format("--------", "-----")
for idx, key in enumerate(sorted(keys)):
buffer += colorize(["%Reset", "%Reset"][idx % 2],
tpl.format(key, self[key]))
return "\n" + buffer + colorize("%Reset")
def update(self, new_dict):
"""Override parent (dict.update()), because it seems that
built-in method doesn't use self.__setitem__() internally,
which is problematic for phpsploit
"""
if isinstance(new_dict, dict):
for key, value in new_dict.items():
self[key] = value
else:
super().update(new_dict)
class VarContainer(MetaDict):
"""VarContainer() object
This class unherits Metadict, and just implements a way to delete
items by settings them to a set of magic values (item_deleters)
>>> obj = VarContainer()
>>> obj['KEY'] = "foobar"
>>> # in this objects, the two following lines do exactly the same thing:
>>> del obj['KEY']
>>> obj['KEY'] = "None"
"""
item_deleters = ["", "NONE"]
def __setitem__(self, name, value):
"""If `value` is None, "None" (case-insensitive) or "" (empty str),
the item is removed instead of being reassigned.
This behavior allows the user to easily remove settings,
env-vars, aliases, and any object unheriting this class,
by simply assiging None to them.
It also raises a KeyError if value is not a valid WORD_TOKEN
"""
if isinstance(value, (str, type(None))) and \
str(value).upper() in self.item_deleters:
if name not in self.keys():
return None
return self.__delitem__(name)
if not WORD_TOKEN.fullmatch(name):
raise KeyError("illegal name: %r doesn't match %s"
% (name, WORD_TOKEN.pattern))
return super().__setitem__(name, value)