ok
Direktori : /opt/imunify360/venv/lib64/python3.11/site-packages/im360/model/ |
Current File : //opt/imunify360/venv/lib64/python3.11/site-packages/im360/model/proactive.py |
import itertools import time from peewee import ( CompositeKey, DoesNotExist, ForeignKeyField, IntegerField, JOIN, PrimaryKeyField, TextField, CharField, ) from defence360agent.model import instance, Model from defence360agent.model.simplification import apply_order_by from im360.model.country import Country from im360.model.firewall import IPList, IPListPurpose class Proactive(Model): """Proactive defense php plugin events.""" class Meta: database = instance.db db_table = "proactive" schema = "proactive" id = PrimaryKeyField() #: When the event happened. timestamp = IntegerField(null=False) #: The IP that made the request - a string representation. ip = TextField(null=True) #: The IP that made the request - a numeric representation. ip_int = IntegerField(null=True) #: The IP that made the request - version (either `4` or `6`). ip_version = IntegerField(null=True) #: A country code for the IP, based on GeoDB data. ip_country_id = CharField(null=True) #: .. deprecated:: 4.6.0 no longer filled after DEF-10708. description = TextField(null=True) #: Action taken, may be `LOG`, `BLOCK`, `KILL`. action = TextField(null=False) #: The name of the server host under which the script was executed. host = TextField(null=True) #: The full PHP script path. path = TextField(null=False) #: The full URL of the request. url = TextField(null=True) #: The number of times the same event happened (if it's aggregated). count = IntegerField(null=False) #: User ID of the process. uid = IntegerField(null=False) #: Group ID of the process. gid = IntegerField(null=False) #: The ID of the matched rule (recognizer sub ID). rule_id = IntegerField(null=True) #: Human-readable name of the matched rule. rule_name = TextField(null=False) @classmethod def _iplist_join(cls): return (IPList.ip == cls.ip) & (~IPList.is_expired()) @classmethod def fetch( cls, uid, since=None, to=None, limit=None, offset=None, search=None, order_by=None, ): q = cls.select( cls.id, cls.timestamp, cls.ip, cls.action, cls.host, cls.path, cls.count, cls.rule_id, cls.rule_name, IPList.listname, ).join(IPList, JOIN.LEFT_OUTER, on=cls._iplist_join()) if uid != 0: q = q.where(cls.uid == uid) if since is not None: q = q.where(cls.timestamp >= since) if to is not None: q = q.where(cls.timestamp <= to) if search is not None: q = q.where( cls.host.contains(search) | cls.path.contains(search) | cls.rule_name.contains(search) | cls.ip.contains(search) ) if order_by is not None: q = apply_order_by(order_by, cls, q) if limit is not None: q = q.limit(limit) if offset is not None: q = q.offset(offset) result = [] for item in q.dicts(): # add new field purpose # but save old listname as backward compatibility item["purpose"] = ( IPListPurpose.listname2purpose(item["listname"]) if item["listname"] else item["listname"] ) result.append(item) return result @classmethod def details(cls, id, uid): q = ( cls.select( cls.id, cls.timestamp, cls.ip, cls.description, cls.url, cls.action, cls.path, cls.count, cls.rule_id, cls.rule_name, IPList.listname, Country.code.alias("country"), ) .join(IPList, JOIN.LEFT_OUTER, on=cls._iplist_join()) .switch() .join( Country, JOIN.LEFT_OUTER, on=(cls.ip_country_id == Country.id) ) .where(cls.id == id) ) if uid != 0: q = q.where(cls.uid == uid) q = q.dicts() if q.count() < 1: raise DoesNotExist event = next(iter(q)) q = ( ProactiveEnv.select(ProactiveEnv.name, ProactiveEnv.value) .where(ProactiveEnv.event_id == event["id"]) .tuples() ) event["env"] = dict(q) event["purpose"] = ( IPListPurpose.listname2purpose(event["listname"]) if event["listname"] else event["listname"] ) return event class ProactiveEnv(Model): """Proactive defence php plugin environment variables.""" #: A reference to the :class:`Proactive` table. event_id = IntegerField(null=False) #: The name of the environment variable. name = TextField(null=False) #: The value of the environment variable. value = TextField(null=True) class Meta: database = instance.db db_table = "proactive_env" schema = "proactive" primary_key = CompositeKey("event_id", "name", "value") class ProactiveIgnoredPath(Model): """Ignore list for proactive defence.""" #: Script path to be ignored. path = TextField(null=False, primary_key=True) #: Timestamp when the ignore record was added. timestamp = IntegerField(null=False, default=time.time) class Meta: database = instance.db db_table = "proactive_ignored_path" @classmethod def _apply_order(cls, q, order_by=None): """ To be able to use itertools.groupby, we need result to be sorted by both path and timestamp, so in this method we add this fields to order_by if they was not passed by caller """ if order_by is None: order_by = [] order = [] fields = cls._meta.sorted_field_names order_by_fields = [f for f, _ in order_by] for field_name, desc in order_by: field = getattr(cls, field_name, None) if field is not None: order.append(field.desc() if desc else field) for field_name in fields: if field_name not in order_by_fields: order.append(getattr(cls, field_name)) order.append(ProactiveIgnoredRule.rule_name) return q.order_by(*order) @classmethod def fetch( cls, limit_homedir=None, since=None, to=None, search=None, order_by=None, limit=50, offset=0, ): q = cls.select( cls.path, cls.timestamp, ProactiveIgnoredRule.rule_id, ProactiveIgnoredRule.rule_name, ).join(ProactiveIgnoredRule, JOIN.LEFT_OUTER) if limit_homedir is not None: # it's ok to use startswith() with path here, # because we are using normalized path when inserting q = q.where(cls.path.startswith(str(limit_homedir))) if search is not None: q = q.where(cls.path.contains(search)) if since is not None: q = q.where(cls.timestamp >= since) if to is not None: q = q.where(cls.timestamp <= to) q = cls._apply_order(q, order_by) result = [] max_count = 0 for p, g in itertools.groupby( q.dicts(), key=lambda r: (r["path"], r["timestamp"]) ): if (max_count >= offset) and (len(result) < limit): path, timestamp = p result.append( { "path": path, "timestamp": timestamp, "rules": [ {"id": row["rule_id"], "name": row["rule_name"]} for row in g if row["rule_id"] is not None ], } ) max_count += 1 return max_count, result class ProactiveIgnoredRule(Model): """Specific rules ignored.""" #: A reference to the :class:`ProactiveIgnoredPath` table. path = ForeignKeyField( ProactiveIgnoredPath, null=False, on_delete="CASCADE", related_name="rules", ) #: The ID of the rule to be ignored. rule_id = IntegerField(null=False) #: A human-readable name of the rule to be ignored (just for information #: purposes - doesn't affect the logic). rule_name = TextField(null=False) class Meta: database = instance.db db_table = "proactive_ignored_rule" indexes = ((("path", "rule_id"), True),)