示例#1
0
class ProjectDatabase(Base):
    __tablename__ = 'projects'

    project_uuid = Column(Integer, primary_key=True, autoincrement=True)
    project_name = Column(String)
    comment = Column(String, default='')

    # Marks whether any user has locked ips (hosts), disallowing adding new ones
    ips_locked = Column(Boolean, default=False)
    hosts_locked = Column(Boolean, default=False)

    date_added = Column(DateTime, default=datetime.datetime.utcnow)

    ips_relationship = relationship('IPDatabase', cascade="all, delete-orphan")
    hosts_relationship = relationship('HostDatabase',
                                      cascade="all, delete-orphan")
    tasks_relationship = relationship('TaskDatabase',
                                      cascade="all, delete-orphan")
    scans_relationship = relationship('ScanDatabase',
                                      cascade="all, delete-orphan")

    session_spawner = Sessions()

    def dict(self):
        return {
            "project_uuid": self.project_uuid,
            "project_name": self.project_name,
            "comment": self.comment,
            "ips_locked": self.ips_locked,
            "hosts_locked": self.hosts_locked,
            "date_added": str(self.date_added)
        }

    @classmethod
    async def create(cls, project_name):
        find_result = await cls.find(project_name=project_name)

        if find_result["status"] == "success":
            if not find_result["projects"]:
                project = cls(project_name=project_name)

                try:
                    with cls.session_spawner.get_session() as session:
                        session.add(project)

                    return {"status": "success", "project": project}
                except Exception as exc:
                    return {"status": "error", "text": str(exc)}

            return {
                "status": "error",
                "text": "A project with that name exists"
            }

        return find_result

    @classmethod
    def _find(cls, project_name=None, project_uuid=None):
        try:
            with cls.session_spawner.get_session() as session:
                project = session.query(cls)

                if project_name is not None:
                    project = project.filter(cls.project_name == project_name)

                if project_uuid is not None:
                    project = project.filter(cls.project_uuid == project_uuid)

                projects = project.all()

                return {"status": "success", "projects": projects}
        except Exception as exc:
            return {"status": "error", "text": str(exc)}

    @classmethod
    async def find(cls, *args, **kwargs):
        return await asyncify(cls._find)(*args, **kwargs)

    @classmethod
    @asyncify
    def delete(cls, project_uuid=None):
        find_result = cls._find(project_uuid=project_uuid)

        if find_result["status"] == "success":
            if find_result["projects"]:
                project = find_result["projects"][0]
                project_uuid = project.project_uuid
                project_name = project.project_name

                try:
                    with cls.session_spawner.get_session() as session:
                        session.delete(project)

                    return {
                        "status": "success",
                        "project_uuid": project_uuid,
                        "project_name": project_name
                    }
                except Exception as exc:
                    return {"status": "error", "text": str(exc)}

            return {
                "status": "error",
                "text": "This project is already deleted"
            }

        # Resend the error which occured during find
        return find_result

    @classmethod
    @asyncify
    def update(cls,
               project_uuid,
               new_name=None,
               new_comment=None,
               ips_locked=None,
               hosts_locked=None):
        find_result = cls._find(project_uuid=project_uuid)

        if find_result["status"] == "success":
            if find_result["projects"]:
                project = find_result["projects"][0]

                try:
                    if new_name is not None:
                        project.project_name = new_name

                    if new_comment is not None:
                        project.comment = new_comment

                    if ips_locked is not None:
                        project.ips_locked = ips_locked

                    if hosts_locked is not None:
                        project.hosts_locked = hosts_locked

                    with cls.session_spawner.get_session() as session:
                        session.add(project)

                        return {"status": "success", "project": project}

                except Exception as exc:
                    return {"status": "error", "text": str(exc)}

            return {"status": "error", "text": "This project no longer exists"}

        # Resend the error which occured during find
        return find_result

    def __repr__(self):
        return "<Project(project_name='%s')>" % (self.project_name)
示例#2
0
class CredDatabase(Base):
    """ Keeps data on the found credentials """
    __tablename__ = "creds"
    __table_args__ = (
        UniqueConstraint(
            'target', 'port_number', 'code', 'candidate', 'mesg', 'project_uuid'
        ),
    )
    # Primary key
    id = Column(Integer, primary_key=True, autoincrement=True)

    # Status code of the request
    code = Column(String)

    # Response size
    size = Column(Integer)
    
    # Time taken to complete the request
    time = Column(String)

    # Login and password divided by ':'
    candidate = Column(String)

    # Number of the current request in the whole task.
    # Not sure we need it, but patator keeps it
    num = Column(Integer)

    # Response message for this request
    mesg = Column(String)

    # Other info from the requester which did not fit to the above fields
    other = Column(String)

    # Name of the module used to bruteforce the target
    # Can be used to extract the name of the service
    service = Column(String)

    # Target (this is either IP or hostname)
    target = Column(String, index=True)

    # Port
    port_number = Column(Integer)

    # ID of the related task (the task, which resulted in the current data)
    task_id = Column(String, ForeignKey('tasks.task_id'))

    # The name of the related project
    project_uuid = Column(
        Integer, ForeignKey('projects.project_uuid', ondelete='CASCADE'), index=True
    )

    # Date of added
    date_added = Column(DateTime, default=datetime.datetime.utcnow)

    session_spawner = Sessions()

    def dict(self):
        return {
            "id": self.id,
            "code": self.code,
            "size": self.size,
            "time": self.time,
            "candidate": self.candidate,
            "num": self.num,
            "mesg": self.mesg,
            "other": self.other,
            "service": self.service,
            "target": self.target,
            "port_number": self.port_number,
            "task_id": self.task_id,
            "project_uuid": self.project_uuid,
            "date_added": str(self.date_added)
        }

    @classmethod
    def create(cls, code=None, size=None, time=None, candidate=None,
               num=None, mesg=None, service=service, target=None,
               port_number=None, task_id=None, project_uuid=None,
               **args
        ):

        db_object = cls(
            code=code,
            size=size,
            time=time,
            candidate=candidate,
            num=num,
            mesg=mesg,
            service=service,
            target=target,
            port_number=port_number,
            task_id=task_id,
            project_uuid=project_uuid,
            other=str(args)
        )

        try:
            with cls.session_spawner.get_session() as session:
                session.add(db_object)
        except IntegrityError:
            return {"status": "success", "target": target}
        except Exception as exc:
            print(str(exc))
            return {"status": "error", "text": str(exc), "target": target}
        else:
            return {"status": "success", "target": target}

    @classmethod
    def find(cls, project_uuid, targets=None, port_number=None):
        try:
            with cls.session_spawner.get_session() as session:
                creds_request = session.query(cls).filter(
                    cls.project_uuid == project_uuid
                )

                if targets:
                    creds_request = creds_request.filter(cls.target.in_(targets))
                if port_number:
                    creds_request = creds_request.filter(cls.port_number == port_number)

                creds = creds_request.all()

                return {"status": "success", "creds": creds}
        except Exception as exc:
            return {"status": "error", "text": str(exc)}

    @classmethod
    def count(cls, project_uuid):
        try:
            with cls.session_spawner.get_session() as session:
                amount = session.query(cls).filter(
                    cls.project_uuid == project_uuid
                ).count()

                return {"status": "success", "amount": amount}
        except Exception as exc:
            return {"status": "error", "text": str(exc)}

    @classmethod
    def delete(cls, project_uuid, targets, port_number):
        try:
            with cls.session_spawner.get_session() as session:
                to_delete_creds = session.query(cls).filter(
                    cls.project_uuid == project_uuid
                )

                if targets:
                    to_delete_creds = to_delete_creds.filter(
                        cls.target.in_(targets)
                    )

                if port_number:
                    to_delete_creds = to_delete_creds.filter(
                        cls.port_number == port_number
                    )

                to_delete_creds.delete('fetch')

                return {"status": "success"}
        except Exception as exc:
            return {"status": "error", "text": str(exc)}
示例#3
0
class TaskDatabase(Base):
    """ Keeps the data of all the tasks that ever existed
    in the system (though not the deleted ones) """
    __tablename__ = 'tasks'

    # Primary key (uuid4)
    task_id = Column(String, primary_key=True)

    # Literal name of the task: dnsscan, nmap etc.
    task_type = Column(String)

    # Target can be: hostname, ip, URL
    #    (depending on the task_type)
    target = Column(String)

    # Params of the lanuched task (flags, dictionaries etc.)
    params = Column(String)

    # {New, Working, Finished, Aborted, ...}
    status = Column(String)

    # Progress in percents
    progress = Column(Integer)

    # Special note. E.x. error
    text = Column(String)

    # Stdout
    stdout = Column(String)

    # Stderr
    stderr = Column(String)

    # Time of adding
    date_added = Column(DateTime, default=datetime.datetime.utcnow)

    # Time of adding
    date_finished = Column(DateTime)

    # The name of the related project
    project_uuid = Column(Integer,
                          ForeignKey('projects.project_uuid',
                                     ondelete='CASCADE'),
                          index=True)

    session_spawner = Sessions()

    def dict(self):
        return {
            "task_id": self.task_id,
            "task_type": self.task_type,
            "target": self.target,
            "params": self.params,
            "status": self.status,
            "progress": self.progress,
            "text": self.text,
            "stdout": self.stdout,
            "stderr": self.stderr,
            "project_uuid": self.project_uuid,
            "date_added": self.date_added,
            "date_finished": self.date_finished
        }

    @classmethod
    @asyncify
    def get_tasks(cls, project_uuid, ips=None, hosts=None):
        try:
            with cls.session_spawner.get_session() as session:
                tasks = session.query(cls)

                if ips is not None:
                    like_filters = list(
                        map(lambda ip: cls.target.like("%,{},%".format(ip)),
                            ips)
                    ) + list(
                        map(lambda ip: cls.target.like("%,{}".format(ip)), ips)
                    ) + list(
                        map(lambda ip: cls.target.like("{},%".format(ip)),
                            ips))

                    tasks = tasks.filter(
                        or_(cls.target.in_(ips), *like_filters))

                if hosts is not None:
                    like_filters = list(
                        map(lambda host: cls.target.like("{}:%".format(host)),
                            hosts))

                    tasks = tasks.filter(
                        or_(
                            # cls.target.in_(hosts),
                            *like_filters))

                if project_uuid is not None:
                    tasks = tasks.filter(cls.project_uuid == project_uuid)

                finished = tasks.filter(
                    or_(cls.status == 'Finished',
                        cls.status == 'Aborted')).all()
                active = tasks.filter(cls.status != 'Finished',
                                      cls.status != 'Aborted').all()

                return {
                    "status": "success",
                    "finished": finished,
                    "active": active
                }
        except Exception as exc:
            return {"status": "error", "text": str(exc)}
示例#4
0
class FileDatabase(Base):
    """ Keeps data on the found file """
    __tablename__ = "files"
    __table_args__ = (
        UniqueConstraint('file_path', 'status_code', 'content_length', 'project_uuid'),
    )

    # Primary key
    file_id = Column(String, primary_key=True)

    # Name of the file
    file_name = Column(String)

    host_id = Column(Integer, ForeignKey('hosts.id'), index=True)

    ip_id = Column(Integer, ForeignKey('ips.id'), index=True)

    # Port
    port_number = Column(Integer)

    # File path
    file_path = Column(String)

    # Status code of that file
    status_code = Column(Integer)

    # Content length of that response
    content_length = Column(String)

    # Special note (for now, we will keep only redirects)
    special_note = Column(String)

    # ID of the related task (the task, which resulted in the current data)
    task_id = Column(String, ForeignKey('tasks.task_id'))

    # The name of the related project
    project_uuid = Column(
        Integer, ForeignKey('projects.project_uuid', ondelete='CASCADE'), index=True
    )

    # Date of added
    date_added = Column(DateTime, default=datetime.datetime.utcnow)

    session_spawner = Sessions()

    @classmethod
    def count(cls, project_uuid):
        with cls.session_spawner.get_session() as session:
            try:
                amount = cls._query_by_project_uuid(session, project_uuid).count()
            except Exception as exc:
                return {
                    "status": "error",
                    "text": str(exc)
                }

        return {
            "status": "success", 
            "amount": amount
        }

    @classmethod
    def _query_by_project_uuid(cls, session, project_uuid):
        return (
            session.query(
                cls
            )
            .filter(
                cls.project_uuid == project_uuid if project_uuid is not None else True
            )
        )

    @classmethod
    def get_stats_for_ips(cls, project_uuid, ip_ids, filters):
        stats = {}

        try:
            with cls.session_spawner.get_session() as session:
                status_code_filters = []
                if filters and filters[0] != '%':
                    status_code_filters.append(FileDatabase.status_code.in_(filters))

                for ip_id in ip_ids:
                    prepared_filters = [FileDatabase.ip_id == ip_id] + status_code_filters  
                    files_stats = (
                        session.query(
                            FileDatabase.status_code,
                            FileDatabase.port_number,
                            func.count(FileDatabase.status_code)
                        )
                        .filter(*prepared_filters)
                        .group_by(
                            FileDatabase.status_code,
                            FileDatabase.port_number
                        )
                        .all()
                    )

                    stats[ip_id] = {}
                    for status_code, port_number, res in files_stats:
                        if port_number not in stats[ip_id]:
                            stats[ip_id][port_number] = {}
                        stats[ip_id][port_number][status_code] = res

                    for port_number, stats_for_port in stats[ip_id].items():
                        stats[ip_id][port_number]['total'] = reduce(
                            lambda x, y: x + y,
                            map(lambda stat: stat[1], stats_for_port.items())
                        )

            return {"status": "success", "stats": stats}
        except Exception as exc:
            return {"status": "error", "text": str(exc)}

    @classmethod
    def get_stats_for_hosts(cls, project_uuid, host_ids, filters):
        stats = {}

        try:
            with cls.session_spawner.get_session() as session:
                status_code_filters = []
                if filters and filters[0] != '%':
                    status_code_filters.append(FileDatabase.status_code.in_(filters))

                for host_id in host_ids:
                    prepared_filters = [FileDatabase.host_id == host_id] + status_code_filters 

                    files_stats = (
                        session.query(
                            FileDatabase.status_code,
                            FileDatabase.port_number,
                            func.count(FileDatabase.status_code)
                        )
                        .filter(*prepared_filters)
                        .group_by(
                            FileDatabase.status_code,
                            FileDatabase.port_number
                        )
                        .all()
                    )

                    stats[host_id] = {}
                    for status_code, port_number, res in files_stats:
                        if port_number not in stats[host_id]:
                            stats[host_id][port_number] = {}
                        stats[host_id][port_number][status_code] = res

                    for port_number, stats_for_port in stats[host_id].items():
                        stats[host_id][port_number]['total'] = reduce(
                            lambda x, y: x + y,
                            map(lambda stat: stat[1], stats_for_port.items())
                        )

            return {"status": "success", "stats": stats}
        except Exception as exc:
            return {"status": "error", "text": str(exc)}

    @classmethod
    def get_files_ip(cls, ip, port_number, limit, offset, filters):
        try:
            files = []

            with cls.session_spawner.get_session() as session:
                prepared_filters = [
                    FileDatabase.ip_id == ip,
                    FileDatabase.port_number == port_number
                ]

                if filters and filters[0] != '%':
                    prepared_filters.append(FileDatabase.status_code.in_(filters))

                files = (
                    session.query(
                        FileDatabase,
                    )
                    .filter(
                        *prepared_filters
                    )
                    .order_by(FileDatabase.status_code, FileDatabase.file_name)
                    .limit(limit)
                    .offset(offset)
                    .all()
                )

            return {"status": "success", "files": files}
        except Exception as exc:
            print(exc)
            return {"status": "error", "text": str(exc)}

    @classmethod
    def get_files_host(cls, host_id, port_number, limit, offset, filters):
        try:
            files = []

            with cls.session_spawner.get_session() as session:
                prepared_filters = [
                    FileDatabase.host_id == host_id,
                    FileDatabase.port_number == port_number
                ]

                if filters and filters[0] != '%':
                    prepared_filters.append(FileDatabase.status_code.in_(filters))

                files = (
                    session.query(
                        FileDatabase,
                    )
                    .filter(
                        *prepared_filters
                    )
                    .order_by(FileDatabase.status_code, FileDatabase.file_name)
                    .limit(limit)
                    .offset(offset)
                    .all()
                )

            return {"status": "success", "files": files}
        except Exception as exc:
            return {"status": "error", "text": str(exc)}


    def dict(self):
        return {
            "file_id": self.file_id,
            "host_id": self.host_id,
            "ip_id": self.ip_id,
            "file_name": self.file_name,
            "port_number": self.port_number,
            "file_path": self.file_path,
            "status_code": self.status_code,
            "content_length": self.content_length,
            "special_note": self.special_note,
            "task_id": self.task_id,
            "project_uuid": self.project_uuid
        }

    def __repr__(self):
        return """
            <FileDatabase(file_id='%s', project_uuid='%s', file_name='%s')>""" % (
            self.file_id, self.project_uuid,
            self.file_name
        )
示例#5
0
class DictDatabase(Base):
    """ Keeps data on the used dictionaries """
    __tablename__ = "dicts"

    # Primary key
    id = Column(Integer, primary_key=True, autoincrement=True)

    # Name of the dictionary, used to store the dict on
    # the disk of container
    name = Column(String)

    # Type of the dictionary. Specifically,
    # the name of the task which will use the dict
    dict_type = Column(String)

    # The content of the dictionary
    content = Column(Text)

    # Amount of lines in the file
    lines_count = Column(Integer)

    # md5 hashsum of the dictionary to make sure we
    # always use the latest version
    hashsum = Column(String)

    # The name of the related project
    project_uuid = Column(Integer,
                          ForeignKey('projects.project_uuid',
                                     ondelete='CASCADE'),
                          index=True)

    # Date of added
    date_added = Column(DateTime, default=datetime.datetime.utcnow)

    session_spawner = Sessions()

    def dict(self):
        return {
            "id": self.id,
            "name": self.name,
            "content": self.content,
            "lines_count": self.lines_count,
            "hashsum": self.hashsum,
            "project_uuid": self.project_uuid,
            "date_added": str(self.date_added)
        }

    @classmethod
    def create(cls,
               name,
               dict_type,
               content,
               project_uuid,
               lines_count=None,
               hashsum=None):

        db_object = cls(name=name,
                        dict_type=dict_type,
                        content=content,
                        project_uuid=project_uuid,
                        lines_count=content.count('\n'),
                        hashsum=hashlib.md5(
                            content.encode('utf-8')).hexdigest())

        try:
            with cls.session_spawner.get_session() as session:
                session.add(db_object)
        except IntegrityError:
            return {"status": "success", "name": name, "dictionary": db_object}
        except Exception as exc:
            return {"status": "error", "text": str(exc), "name": name}
        else:
            return {"status": "success", "name": name, "dictionary": db_object}

    @classmethod
    def get(cls, project_uuid=None, dict_id=None, name=None):
        try:
            with cls.session_spawner.get_session() as session:
                dict_request = session.query(
                    # cls.id,
                    # cls.name,
                    # cls.dict_type,
                    # # cls.content, ## This is too big to keep in memory
                    # cls.lines_count,
                    # cls.hashsum,
                    # cls.project_uuid
                    cls)

                if project_uuid:
                    dict_request = dict_request.filter(
                        cls.project_uuid == project_uuid)
                if dict_id:
                    dict_request = dict_request.filter(cls.id == dict_id)
                if name:
                    dict_request = dict_request.filter(cls.name == name)

                dicts = dict_request.all()

                return {"status": "success", "dicts": dicts}
        except Exception as exc:
            return {"status": "error", "text": str(exc)}

    @classmethod
    def count(cls, project_uuid):
        try:
            with cls.session_spawner.get_session() as session:
                amount = session.query(cls).filter(
                    cls.project_uuid == project_uuid).count()

                return {"status": "success", "amount": amount}
        except Exception as exc:
            return {"status": "error", "text": str(exc)}

    @classmethod
    def delete(cls, project_uuid, dict_id=None, name=None):
        try:
            with cls.session_spawner.get_session() as session:
                to_delete_dict = session.query(cls).filter(
                    cls.project_uuid == project_uuid)

                if dict_id:
                    to_delete_dict = to_delete_dict.filter(cls.id == id)

                if name:
                    to_delete_dict = to_delete_dict.filter(cls.name == name)

                to_delete_dict.delete('fetch')

                return {"status": "success", "dict_id": to_delete_dict.dict_id}
        except Exception as exc:
            return {"status": "error", "text": str(exc)}
示例#6
0
class HostDatabase(Base):
    """ Keeps hosts that point to relative IPs """
    __tablename__ = 'hosts'
    __table_args__ = (UniqueConstraint('target', 'project_uuid'), )

    # Primary key (probably uuid4)
    id = Column(Integer, primary_key=True, autoincrement=True)

    # IP address is a string (probably None, but not sure if
    #    is needed)
    target = Column(String)

    # Comment field, as requested by VI
    comment = Column(String, default="")

    # A list of files which is associated with the current scope
    files = relationship('FileDatabase',
                         cascade="all, delete-orphan",
                         lazy='select')

    # The name of the related project
    project_uuid = Column(Integer,
                          ForeignKey('projects.project_uuid',
                                     ondelete="CASCADE"),
                          index=True)

    # References the task that got this record
    # Default is None, as scope can be given by the user manually.
    task_id = Column(String,
                     ForeignKey('tasks.task_id', ondelete='SET NULL'),
                     default=None)

    # Date of adding
    date_added = Column(DateTime, default=datetime.datetime.utcnow)

    # IP addresses of that host
    ip_addresses = relationship("IPDatabase",
                                secondary=association_table,
                                back_populates="hostnames",
                                lazy='noload')

    __mapper_args__ = {'concrete': True}

    session_spawner = Sessions()

    @classmethod
    def _find(cls, target, project_uuid):
        """ Finds scope (host or ip) in the database """

        with cls.session_spawner.get_session() as session:
            scope_from_db = session.query(cls).filter(
                cls.project_uuid == project_uuid,
                cls.target == target).one_or_none()

            return scope_from_db

    @classmethod
    async def find(cls, *args, **kwargs):
        return await asyncio.get_event_loop().run_in_executor(
            None, lambda: cls._find(*args, **kwargs))

    @classmethod
    @asyncify
    def create(cls, target, project_uuid, task_id=None):
        """ Creates a new scope if it is not in the db yet """

        if cls._find(target, project_uuid) is None:
            try:
                new_scope = cls(target=target,
                                project_uuid=project_uuid,
                                task_id=task_id)

                with cls.session_spawner.get_session() as session:
                    session.add(new_scope)
            except Exception as exc:
                return {"status": "error", "text": str(exc)}
            else:
                return {"status": "success", "new_scope": new_scope}

        return {"status": "duplicate", "text": "duplicate"}

    @classmethod
    @asyncify
    def get_or_create(cls, target, project_uuid, task_id=None):
        """ Returns a tuple (new_scope, created).
        Second value:
            False if existed
            True if created
        """
        found = cls._find(target, project_uuid)

        if found is None:
            try:
                new_scope = cls(target=target,
                                project_uuid=project_uuid,
                                task_id=task_id)

                with cls.session_spawner.get_session() as session:
                    session.add(new_scope)
            except Exception as exc:
                return (None, None)
            else:
                return (new_scope, True)
        return (found, False)

    @classmethod
    @asyncify
    def update(cls, scope_id, comment):
        try:
            with cls.session_spawner.get_session() as session:
                db_object = session.query(cls).filter(cls.id == scope_id).one()
                target = db_object.target
                db_object.comment = comment
                session.add(db_object)
        except Exception as exc:
            return {"status": "error", "text": str(exc)}
        else:
            return {"status": "success", "target": target}

    @classmethod
    def count(cls, project_uuid):
        with cls.session_spawner.get_session() as session:
            return session.query(cls).filter(
                cls.project_uuid == project_uuid).count()

    @classmethod
    @asyncify
    def delete_scope(cls, scope_id):
        """ Deletes scope by its id """

        try:
            with cls.session_spawner.get_session() as session:
                db_object = (session.query(HostDatabase).filter(
                    HostDatabase.id == scope_id).options(
                        joinedload(HostDatabase.ip_addresses)).one())

                target = db_object.target

                session.delete(db_object)
        except Exception as exc:
            print(str(exc))
            return {"status": "error", "text": str(exc), "target": scope_id}
        else:
            return {"status": "success", "target": target}

    def dict(self,
             include_ports=False,
             include_ips=False,
             include_files=False,
             files_statsified=False):
        return {
            "host_id":
            self.id,
            "hostname":
            self.target,
            "comment":
            self.comment,
            "project_uuid":
            self.project_uuid,
            "task_id":
            self.task_id,
            "ip_addresses":
            list(
                map(
                    lambda hostname: hostname.dict(include_ports=include_ports
                                                   ), self.ip_addresses))
            if include_ips else [],
            "files": []
        }

    def __repr__(self):
        return """<HostDatabase(host_id='%s', hostname='%s',
                        ip_addresses='%s', project_uuid='%s')>""" % (
            self.id, self.target, self.ip_addresses, self.project_uuid)