class User(db.Model, flask_login.UserMixin): """user model""" id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(250), unique=True, nullable=False) _password = db.Column(db.String(250), name='password') email = db.Column(db.String(250)) active = db.Column(db.Boolean, nullable=False, default=False) roles = db.Column(postgresql.ARRAY(db.String, dimensions=1), nullable=False, default=[]) _apikey = db.Column(db.String(250), name='apikey') totp = db.Column(db.String(32)) webauthn_credentials = relationship('WebauthnCredential', back_populates='user', cascade='delete,delete-orphan', passive_deletes=True) @property def is_active(self): """user active getter""" return self.active def has_role(self, role): """shortcut function to check user has role""" if self.roles and (role in self.roles): return True return False @hybrid_property def password(self): """password getter""" return self._password @password.setter def password(self, value): """password setter; condition is handling value edit from empty form.populate_obj submission""" if value: self._password = PWS().hash(value) @hybrid_property def apikey(self): """apikey getter""" return self._apikey @apikey.setter def apikey(self, value): """apikey setter""" self._apikey = PWS.hash_simple(value) if value else None def __repr__(self): return '<User %s: %s>' % (self.id, self.username)
class Task(db.Model): """holds settings/arguments for type of scan/scanner. eg. host discovery, fast portmap, version scan, ...""" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(250), nullable=False) module = db.Column(db.String(250), nullable=False) params = db.Column(db.Text) queues = relationship('Queue', back_populates='task', cascade='delete,delete-orphan', passive_deletes=True) def __repr__(self): return '<Task %d: %s>' % (self.id, self.name)
class WebauthnCredential(db.Model): """Webauthn credential model""" id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'), nullable=False) user_handle = db.Column(db.String(64), nullable=False) credential_data = db.Column(db.LargeBinary, nullable=False) name = db.Column(db.String(250)) registered = db.Column(db.DateTime, default=datetime.utcnow) user = relationship('User', back_populates='webauthn_credentials') def __repr__(self): return '<WebauthnCredential %s: %s>' % (self.id, self.user_id)
class Queue(db.Model): """task assignment for specific targets""" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(250), nullable=False) task_id = db.Column(db.Integer, db.ForeignKey('task.id', ondelete='CASCADE'), nullable=False) group_size = db.Column(db.Integer, nullable=False) priority = db.Column(db.Integer, nullable=False) active = db.Column(db.Boolean, nullable=False, default=False) task = relationship('Task', back_populates='queues') targets = relationship('Target', back_populates='queue', cascade='delete,delete-orphan', passive_deletes=True) jobs = relationship('Job', back_populates='queue', cascade='delete,delete-orphan', passive_deletes=True) def __repr__(self): return '<Queue %d: %s>' % (self.id, self.name) @property def data_abspath(self): """return absolute path of the queue data directory""" return os.path.join(current_app.config['SNER_VAR'], 'scheduler', 'queue-%s' % self.id) if self.id else None
class Service(db.Model): """discovered host service""" id = db.Column(db.Integer, primary_key=True) host_id = db.Column(db.Integer, db.ForeignKey('host.id', ondelete='CASCADE'), nullable=False) proto = db.Column(db.String(250), nullable=False) port = db.Column(db.Integer, nullable=False) state = db.Column(db.String(250)) name = db.Column(db.String(250)) info = db.Column(db.Text) tags = db.Column(postgresql.ARRAY(db.String, dimensions=1), nullable=False, default=[]) comment = db.Column(db.Text) host = relationship('Host', back_populates='services') vulns = relationship('Vuln', back_populates='service', cascade='delete,delete-orphan', passive_deletes=True) notes = relationship('Note', back_populates='service', cascade='delete,delete-orphan', passive_deletes=True) def __repr__(self): return '<Service %s: %s.%d>' % (self.id, self.proto, self.port)
class Vuln(db.Model): """vulnerability model; heavily inspired by metasploit; hdm rulez""" id = db.Column(db.Integer, primary_key=True) host_id = db.Column(db.Integer, db.ForeignKey('host.id', ondelete='CASCADE'), nullable=False) service_id = db.Column(db.Integer, db.ForeignKey('service.id', ondelete='CASCADE')) name = db.Column(db.String(1000), nullable=False) xtype = db.Column(db.String(250)) severity = db.Column(db.Enum(SeverityEnum), nullable=False) descr = db.Column(db.Text) data = db.Column(db.Text) refs = db.Column(postgresql.ARRAY(db.String, dimensions=1), nullable=False, default=[]) tags = db.Column(postgresql.ARRAY(db.String, dimensions=1), nullable=False, default=[]) comment = db.Column(db.Text) host = relationship('Host', back_populates='vulns') service = relationship('Service', back_populates='vulns') def __repr__(self): return '<Vuln %s: %s>' % (self.id, self.xtype)
class Note(db.Model): """host assigned note, generic data container""" id = db.Column(db.Integer, primary_key=True) host_id = db.Column(db.Integer, db.ForeignKey('host.id', ondelete='CASCADE'), nullable=False) service_id = db.Column(db.Integer, db.ForeignKey('service.id', ondelete='CASCADE')) xtype = db.Column(db.String(250)) data = db.Column(db.Text) tags = db.Column(postgresql.ARRAY(db.String, dimensions=1), nullable=False, default=[]) comment = db.Column(db.Text) host = relationship('Host', back_populates='notes') service = relationship('Service', back_populates='notes') def __repr__(self): return '<Note %s: %s>' % (self.id, self.xtype)
class Host(db.Model): """basic host (ip-centric) model""" id = db.Column(db.Integer, primary_key=True) address = db.Column(postgresql.INET, nullable=False) hostname = db.Column(db.String(256)) os = db.Column(db.Text) tags = db.Column(postgresql.ARRAY(db.String, dimensions=1), nullable=False, default=[]) comment = db.Column(db.Text) created = db.Column(db.DateTime, default=datetime.utcnow) modified = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) services = relationship('Service', back_populates='host', cascade='delete,delete-orphan', passive_deletes=True) vulns = relationship('Vuln', back_populates='host', cascade='delete,delete-orphan', passive_deletes=True) notes = relationship('Note', back_populates='host', cascade='delete,delete-orphan', passive_deletes=True) def __repr__(self): return '<Host %s: %s (%s)>' % (self.id, self.address, self.hostname if self.hostname else '')
class Job(db.Model): """assigned job""" id = db.Column(db.String(36), primary_key=True) queue_id = db.Column(db.Integer, db.ForeignKey('queue.id', ondelete='CASCADE')) assignment = db.Column(db.Text, nullable=False) retval = db.Column(db.Integer) time_start = db.Column(db.DateTime, default=datetime.utcnow) time_end = db.Column(db.DateTime) queue = relationship('Queue', back_populates='jobs') def __repr__(self): return '<Job %s>' % self.id @property def output_abspath(self): """return absolute path to the output data file acording to current app config""" return os.path.join(self.queue.data_abspath, self.id)