diff --git a/README.md b/README.md index e0a0c8d2..e7e5ce30 100644 --- a/README.md +++ b/README.md @@ -59,25 +59,20 @@ Description in progress Sarenka is local web application for Windows. #### Config -Rirst release gathers data from two search engines. -example sarenka/backend/connectors/credentials.json - +User have to crete account on services: +censys: +shodan: +Next on **/api/user_credentials** via post request user can add credentials: ```json -{ - "censys": { - "base_url": "https://censys.io/", - "API_ID": "", - "Secret": "", - "API_URL": "https://censys.io/api/v1" - }, - "shodan": { - "base_url": "https://www.shodan.io/", - "user": "", - "api_key": "" - } +{ + "censys.api_id": "", + "censys.secret" : "", + "shodan.user": "", + "shodan.api_key": "" } ``` + # Features - gets data from **https://censys.io/** by ip - get data from **https://www.shodan.io/** by ip diff --git a/sarenka/backend/api_searcher/migrations/0007_auto_20210108_1321.py b/sarenka/backend/api_searcher/migrations/0007_auto_20210108_1321.py new file mode 100644 index 00000000..72615c98 --- /dev/null +++ b/sarenka/backend/api_searcher/migrations/0007_auto_20210108_1321.py @@ -0,0 +1,21 @@ +# Generated by Django 3.1.4 on 2021-01-08 12:21 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api_searcher', '0006_censyscredentailsmodel_shodancredentailsmodel'), + ] + + operations = [ + migrations.RenameModel( + old_name='CensysCredentailsModel', + new_name='CensysCredentialsModel', + ), + migrations.RenameModel( + old_name='ShodanCredentailsModel', + new_name='ShodanCredentialsModel', + ), + ] diff --git a/sarenka/backend/api_searcher/models.py b/sarenka/backend/api_searcher/models.py index a0c93b01..57bf4042 100644 --- a/sarenka/backend/api_searcher/models.py +++ b/sarenka/backend/api_searcher/models.py @@ -4,7 +4,7 @@ from django.db import models -class CensysCredentailsModel(models.Model): +class CensysCredentialsModel(models.Model): """Model przechowujący informacje o danych uwierzytelniajacych użytkownika do serwisu http://censys.io/ Może istnieć tylko jedna instancja przechowująca dane użytkownika""" api_id = models.CharField(max_length=72, unique=True, default="") @@ -21,7 +21,7 @@ def save(self, *args, **kwargs): return super().save(*args, **kwargs) -class ShodanCredentailsModel(models.Model): +class ShodanCredentialsModel(models.Model): """Model przechowujący informacje o danych uwierzytelniajacych użytkownika do serwisu https://www.shodan.io/ Może istnieć tylko jedna instancja przechowująca dane użytkownika""" user = models.CharField(max_length=200, unique=True, default="") diff --git a/sarenka/backend/api_searcher/search_engines/censys_engine/censys_credentials.py b/sarenka/backend/api_searcher/search_engines/censys_engine/censys_credentials.py index 7f0d1416..7c384206 100644 --- a/sarenka/backend/api_searcher/search_engines/censys_engine/censys_credentials.py +++ b/sarenka/backend/api_searcher/search_engines/censys_engine/censys_credentials.py @@ -1,4 +1,7 @@ -from api_searcher.models import CensysCredentailsModel +from django.conf import settings +USER_CREDENTIALS_DB = settings.USER_CREDENTIALS_DB_NAME + +from api_searcher.models import CensysCredentialsModel class CensysCredentialsError(Exception): """Zgłasza wyjątek gdy nie można utworzyć obiketu przechowujące dane użytkownika do seriwsu http://censys.io/""" @@ -8,26 +11,36 @@ def __init__(self, message=None, errors=None): class CensysCredentials: - """Klasa przechowująca wymagane dane dla seriwsu trzeciego http://censys.io/. + """Sinleton - Klasa przechowująca wymagane dane dla seriwsu trzeciego http://censys.io/. Daje także możliwość aktualizacji danych uwierzytelniających użytkownika np. w przypadku przekroczenia ilości wyszukiwań na darmowym koncie w serwisie.""" + __instance = None + + def __init__(self): + if not CensysCredentials.__instance: + credentials_obj = CensysCredentialsModel.objects.using(self.db_name).all().first() + if not credentials_obj: + credentials_obj = CensysCredentialsModel.objects.using(self.db_name).create() - def __init__(self, credentials_db_name:str): - # obiekt bazy danych z danymi uwierzytelniajacymi użytkownika - self.__db_name = credentials_db_name + self.__base_url = credentials_obj.base_url + self.__api_id = credentials_obj.api_id + self.__secret = credentials_obj.secret + self.__api_url = credentials_obj.api_url + else: + self.getInstance() - credentials_obj = CensysCredentailsModel.objects.using(self.credentials_db_name).all().first() - if not credentials_obj: - credentials_obj = CensysCredentailsModel.objects.using(self.credentials_db_name).create() + @classmethod + def getInstance(cls): + """Metoda klasy wymaga dla klasy typu Singleton + - zwraca instancję klasy, gwarantuje istnienie tylko jednego obiektu z danymi wuierzytleniajacmi użytkownika.""" + if not cls.__instance: + cls.__instance = CensysCredentials() + return cls.__instance - self.__base_url = credentials_obj.base_url - self.__api_id = credentials_obj.api_id - self.__secret = credentials_obj.secret - self.__api_url = credentials_obj.api_url @property - def credentials_db_name(self): - return self.__db_name + def db_name(self): + return USER_CREDENTIALS_DB @property def base_url(self): @@ -39,7 +52,10 @@ def api_id(self): def update_api_id(self, new_api_id): """Metoda do aktualizacji danych "API_ID" dla konta użytkownika do serwisu http://censys.io/ """ - credentials_obj = CensysCredentailsModel.objects.using(self.credentials_db_name).first() + if not new_api_id: + raise CensysCredentialsError('Censys "API_ID" value is empty.') + + credentials_obj = CensysCredentialsModel.objects.using(self.db_name).all().first() credentials_obj.api_id = new_api_id credentials_obj.save() @@ -52,7 +68,10 @@ def secret(self): def update_secret(self, new_secret): """Metoda niezbędne do aktualizacji danych "Secret" dla konta użytkownika do serwisu http://censys.io/ """ - credentials_obj = CensysCredentailsModel.objects.using(self.credentials_db_name).all().first() + if not new_secret: + raise CensysCredentialsError('Censys "Secret" value is empty.') + + credentials_obj = CensysCredentialsModel.objects.using(self.db_name).all().first() credentials_obj.secret = new_secret credentials_obj.save() diff --git a/sarenka/backend/api_searcher/search_engines/shodan_engine/shodan_credentials.py b/sarenka/backend/api_searcher/search_engines/shodan_engine/shodan_credentials.py index df2017c0..8c7f8512 100644 --- a/sarenka/backend/api_searcher/search_engines/shodan_engine/shodan_credentials.py +++ b/sarenka/backend/api_searcher/search_engines/shodan_engine/shodan_credentials.py @@ -1,4 +1,7 @@ -from api_searcher.models import ShodanCredentailsModel +from django.conf import settings +USER_CREDENTIALS_DB = settings.USER_CREDENTIALS_DB_NAME + +from api_searcher.models import ShodanCredentialsModel class ShodanCredentialsError(Exception): @@ -9,26 +12,35 @@ def __init__(self, message=None, errors=None): class ShodanCredentials: - """Klasa przechowująca wymagane dane dla seriwsu trzeciego https://shodan.io/. + """Singleton - Klasa przechowująca wymagane dane dla seriwsu trzeciego https://shodan.io/. Daje także możliwość aktualizacji danych uwierzytelniających użytkownika np. w przypadku przekroczenia ilości wyszukiwań na darmowym koncie w serwisie.""" - def __init__(self, credentials_db_name:str): - print("self.credentials_db_name: ", credentials_db_name) - self.__db_name = credentials_db_name + __instance = None - credentials_obj = ShodanCredentailsModel.objects.using(self.credentials_db_name).all().first() - if not credentials_obj: - credentials_obj = ShodanCredentailsModel.objects.using(self.credentials_db_name).create() + def __init__(self): + if not ShodanCredentials.__instance: + credentials_obj = ShodanCredentialsModel.objects.using(self.db_name).all().first() + if not credentials_obj: + credentials_obj = ShodanCredentialsModel.objects.using(self.db_name).create() - self.__base_url = credentials_obj.base_url - self.__api_key = credentials_obj.api_key - self.__user = credentials_obj.user + self.__base_url = credentials_obj.base_url + self.__api_key = credentials_obj.api_key + self.__user = credentials_obj.user + else: + self.getInstance() + @classmethod + def getInstance(cls): + """Metoda klasy wymaga dla klasy typu Singleton + - zwraca instancję klasy, gwarantuje istnienie tylko jednego obiektu z danymi wuierzytleniajacmi użytkownika.""" + if not cls.__instance: + cls.__instance = ShodanCredentials() + return cls.__instance @property - def credentials_db_name(self): - return self.__db_name + def db_name(self): + return USER_CREDENTIALS_DB @property def base_url(self): @@ -40,7 +52,10 @@ def api_key(self): def update_api_key(self, new_api_key): """Metoda do aktualizacji danych "user" dla konta użytkownika do serwisu https://shodan.io/ """ - credentials_obj = ShodanCredentailsModel.objects.using(self.credentials_db_name).all().first() + if not new_api_key: + raise ShodanCredentialsError('Shodan "api_key" value is empty.') + + credentials_obj = ShodanCredentialsModel.objects.using(self.db_name).all().first() credentials_obj.api_key = new_api_key credentials_obj.save() @@ -53,9 +68,13 @@ def user(self): def update_user(self, new_user): """Metoda do aktualizacji danych "api_key" dla konta użytkownika do serwisu https://shodan.io/""" - credentials_obj = ShodanCredentailsModel.objects.using(self.credentials_db_name).all().first() + if not new_user: + raise ShodanCredentialsError('Shodan "user" value is empty.') + + credentials_obj = ShodanCredentialsModel.objects.using(self.db_name).all().first() credentials_obj.user = new_user credentials_obj.save() # aktualizacja danych obiektu self.__user = new_user + diff --git a/sarenka/backend/api_searcher/search_engines/shodan_engine/shodan_host_search.py b/sarenka/backend/api_searcher/search_engines/shodan_engine/shodan_host_search.py index 9b4d6725..f9ec9aee 100644 --- a/sarenka/backend/api_searcher/search_engines/shodan_engine/shodan_host_search.py +++ b/sarenka/backend/api_searcher/search_engines/shodan_engine/shodan_host_search.py @@ -23,5 +23,5 @@ def get_data(self, ip_address): connector = ShodanConnector(shodan_credentials) response = connector.search_by_ip(ip_address) return response.to_json # TODO zmienić na serializatory - except ShodanHostSearchError as ex: + except ShodanCredentialsError as ex: raise ShodanHostSearchError("Invalid settings for service https://censys.io/. " + str(ex)) diff --git a/sarenka/backend/api_searcher/search_engines/user_credentials.py b/sarenka/backend/api_searcher/search_engines/user_credentials.py index c0aa2ad2..72131a47 100644 --- a/sarenka/backend/api_searcher/search_engines/user_credentials.py +++ b/sarenka/backend/api_searcher/search_engines/user_credentials.py @@ -2,10 +2,8 @@ Moduł do przechowywania danych użytkownika takich jak klucze do serwisów, które wymagają kont dla korzystania z ich api i/lub funkcjonalności. """ -import json from .censys_engine.censys_credentials import CensysCredentials, CensysCredentialsError from .shodan_engine.shodan_credentials import ShodanCredentials, ShodanCredentialsError -import os class UserCredentialsError(Exception): @@ -20,22 +18,16 @@ def __init__(self, message=None, errors=None): class UserCredentials: """Singleton - Klasa przechowująca dane użytkownika do serwisów trzecich niezbędne do korzystania z ich funkcjonalności.""" __instance = None - __db_name = "user_credentials" - - @property - def db_name(self): - """Metoda klasy zwracajaca ścieżkę do pliku konfiguracyjnego użytkownika.""" - return self.__db_name def __init__(self): if not UserCredentials.__instance: try: - self.__censys = CensysCredentials(self.db_name) + self.__censys = CensysCredentials() except CensysCredentialsError as ex: raise UserCredentialsError(str(ex)) try: - self.__shodan = ShodanCredentials(self.db_name) + self.__shodan = ShodanCredentials() except ShodanCredentialsError as ex: raise UserCredentialsError(str(ex)) else: diff --git a/sarenka/backend/api_searcher/search_engines/user_credentials_updater.py b/sarenka/backend/api_searcher/search_engines/user_credentials_updater.py index e8158a09..9acd7ae5 100644 --- a/sarenka/backend/api_searcher/search_engines/user_credentials_updater.py +++ b/sarenka/backend/api_searcher/search_engines/user_credentials_updater.py @@ -1,6 +1,3 @@ -import json -import os - from .user_credentials import UserCredentials, UserCredentialsError @@ -24,7 +21,6 @@ def __init__(self, user_data): def user_data(self): return self.__user_data - def __update_censys_credentials(self): """Metoda pomocnicza aktualizująca dane uwierzytelniające użytkownika do serwisu http://censys.io/ Aktualizuje obiekt przechowujący dane oraz plik konfiguracyjny.""" @@ -36,7 +32,6 @@ def __update_censys_credentials(self): self.user_credentials.censys.update_api_id(new_api_id) self.user_credentials.censys.update_secret(new_secret) - def __update_shodan_credentials(self): """Metoda pomocnicza aktualizująca dane uwierzytelniające użytkownika do serwisu https://shodan.io/ Aktualizuje obiekt przechowujący dane oraz plik konfiguracyjny.""" @@ -47,7 +42,6 @@ def __update_shodan_credentials(self): self.user_credentials.shodan.update_user(new_shodan_user) self.user_credentials.shodan.update_api_key(new_shodan_api_key) - def update(self): """Metoda aktualizująca i zapisujaca do pliku dane uwierzytelniające użytkownika do serwisów trzeich, które udostępniaja swoje dane tylko po utworzeniu konta z uniklanym kluczem przypisanym użytkownikowi.""" @@ -56,3 +50,4 @@ def update(self): self.__update_shodan_credentials() except Exception as ex: raise UserCredentialsUpdaterError(str(ex)) + diff --git a/sarenka/backend/api_searcher/searcher_full.py b/sarenka/backend/api_searcher/searcher_full.py index 0aef74b3..a0b81862 100644 --- a/sarenka/backend/api_searcher/searcher_full.py +++ b/sarenka/backend/api_searcher/searcher_full.py @@ -48,7 +48,7 @@ def get_censys_data(self): raise CredentialsNotFoundError("UserCredentials object does not exist.") except CredentialsNotFoundError as ex: - settings_url = self.host_address + reverse('settings') + settings_url = self.host_address + reverse("user_credentials") return { "censys": { "error": "Unable to get credentials for service http://censys.io/. " @@ -77,7 +77,7 @@ def get_shodan_data(self): raise CredentialsNotFoundError("UserCredentials object does not exist.") except CredentialsNotFoundError as ex: - settings_url = self.host_address + reverse('settings') + settings_url = self.host_address + reverse("user_credentials") return { "shodan": { "error": "Unable to get credentials for service https://www.shodan.io/. " diff --git a/sarenka/backend/api_searcher/serializers.py b/sarenka/backend/api_searcher/serializers.py index 9c97e406..8d842f7f 100644 --- a/sarenka/backend/api_searcher/serializers.py +++ b/sarenka/backend/api_searcher/serializers.py @@ -1,12 +1,29 @@ from rest_framework import serializers +from django.shortcuts import get_object_or_404 +from .models import CensysCredentialsModel, ShodanCredentialsModel + + +class CensysCredentailsSerializer(serializers.ModelSerializer): + """Serializer dla danych uwierzytelniajacych użytkownika do serwisu https://censys.io/""" + class Meta: + model = CensysCredentialsModel + fields = ["api_id", "secret"] + + + +class ShodanCredentailsSerializer(serializers.ModelSerializer): + """Serializer dla danych uwierzytelniajacych użytkownika do serwisu https://www.shodan.io/""" + class Meta: + model = ShodanCredentialsModel + fields = ["user", "api_key"] class UserCredentialsSerializer(serializers.Serializer): """Serializuje klucze użytkownika do serwisów trzeich jak censys.io oraz shodan.io""" - censys_API_ID = serializers.CharField(max_length=72) - censys_Secret = serializers.CharField(max_length=64) - shodan_user = serializers.CharField(max_length=200) - shodan_api_key = serializers.CharField(max_length=64) + censys = CensysCredentailsSerializer(read_only=False, many=False) + shodan = ShodanCredentailsSerializer(read_only=False, many=False) + + class ProductSerializer(serializers.Serializer): vendor = serializers.CharField() diff --git a/sarenka/backend/api_searcher/tests/test_a_record.py b/sarenka/backend/api_searcher/tests/test_a_record.py index 7f8cb190..e69de29b 100644 --- a/sarenka/backend/api_searcher/tests/test_a_record.py +++ b/sarenka/backend/api_searcher/tests/test_a_record.py @@ -1,31 +0,0 @@ -import unittest - -from api_searcher.a_record import ARecord, ARecordWrongFQDNError - - -class TestARecord(unittest.TestCase): - # TODO: mocki i refaktor - - def test_get_ip(self): - ip_val = ARecord.get_ip('tutorialspoint.com') - self.assertEqual(ip_val, ['95.217.74.146']) - - ip_val = ARecord.get_ip('renmich.faculty.wmi.amu.edu.pl') - self.assertEqual(ip_val, ['150.254.78.21']) - - with self.assertRaises(ARecordWrongFQDNError): - ARecord.get_ip('https://marcing.faculty.wmi.amu.edu.pl/DSIK/cwiczenia5.html') - - def test_get_cname(self): - cname_val = ARecord.get_cname('mail.google.com') - self.assertIsNotNone(cname_val) - - cname_val = ARecord.get_cname('renmich.faculty.wmi.amu.edu.pl') - self.assertIsNotNone(cname_val) - - with self.assertRaises(ARecordWrongFQDNError): - ARecord.get_cname('https://marcing.faculty.wmi.amu.edu.pl/DSIK/cwiczenia5.html') - - -if __name__ == "__main__": - unittest.main() \ No newline at end of file diff --git a/sarenka/backend/api_searcher/tests/test_api_search_urls.py b/sarenka/backend/api_searcher/tests/test_api_search_urls.py index 2fd56ca3..e69de29b 100644 --- a/sarenka/backend/api_searcher/tests/test_api_search_urls.py +++ b/sarenka/backend/api_searcher/tests/test_api_search_urls.py @@ -1,7 +0,0 @@ -from django.urls import reverse, resolve - - -class TestApiDNSUrls: - def test_a_record_url(self): - path = reverse('dns_record', kwargs={"fqdn": "test_fqdn"}) - assert resolve(path).view_name == "dns_record" \ No newline at end of file diff --git a/sarenka/backend/api_searcher/tests/test_searcher_urls.py b/sarenka/backend/api_searcher/tests/test_searcher_urls.py index ddbb7d47..e69de29b 100644 --- a/sarenka/backend/api_searcher/tests/test_searcher_urls.py +++ b/sarenka/backend/api_searcher/tests/test_searcher_urls.py @@ -1,7 +0,0 @@ -from django.urls import reverse, resolve - - -class TestSearcherUrls: - def test_cve_url(self): - path = reverse('get_by_cve', kwargs={"cve_id": "CVE-2010-3333"}) - assert resolve(path).view_name == "get_by_cve" \ No newline at end of file diff --git a/sarenka/backend/api_searcher/tests/test_views.py b/sarenka/backend/api_searcher/tests/test_views.py index 21177fc7..e69de29b 100644 --- a/sarenka/backend/api_searcher/tests/test_views.py +++ b/sarenka/backend/api_searcher/tests/test_views.py @@ -1,78 +0,0 @@ -from django.test import RequestFactory -from django.urls import reverse -from mixer.backend.django import mixer -import pytest -from django.contrib.auth.models import User, AnonymousUser -from django.test import TestCase - -from api_searcher.views import ARecordView, login_required_view - - -@pytest.mark.django_db -class TestViews(TestCase): - #pytest --cov - #pytest --cov=. #można sprawdzic sciezke ile w danym miejscu pokryte testami - - @classmethod - def setUpClass(cls): - super(TestViews, cls).setUpClass() - # mixer.blend('moje_api.NazwaModelu') # #jakby testowac z bazy - cls.factory = RequestFactory() - - def test_login_required_view_authenticated(self): - # mixer.blend('moje_api.NazwaModelu') # #jakby testowac z bazy - path = reverse('login_required_view', kwargs={"cve_code": "CVE-2010-3333"}) - request = self.factory.get(path) - request.user = mixer.blend(User) - - response = login_required_view(request, cve_code="CVE-2010-3333") - assert response.status_code == 200 - - def test_login_required_view_unauthenticated(self): - # mixer.blend('moje_api.NazwaModelu') # #jakby testowac z bazy - path = reverse('login_required_view', kwargs={"cve_code": "CVE-2010-3333"}) - request = self.factory.get(path) - request.user = AnonymousUser() - - response = login_required_view(request, cve_code="CVE-2010-3333") - assert response.status_code == 302 - # testowanie przekierowania żeby user się zalogował (sprawdza url który pokazuje się niezalogowanemu uzytkownikowi - # assert 'accounts/login' in response.url - - - -# uzcie fixtures - -@pytest.fixture(scope='module') #zeby tylko raz dla testow utworzyl ten obiekt -def factory(): - return RequestFactory() - -@pytest.fixture() -def my_model_object(db): - """ - jak ma w fixture argument db to nie treba juz go przekazywac do wlasciwych metod testow - """ - # return mixer.blend('api_searcher.MyModel') - pass - - -# def test_login_required_view_authenticated(factory, my_model_object, db):#db argument na koncu -def test_login_required_view_authenticated(factory, my_model_object): - # mixer.blend('moje_api.NazwaModelu') # #jakby testowac z bazy - path = reverse('login_required_view', kwargs={"cve_code": "CVE-2010-3333"}) - request = factory.get(path) # TUUUUUUUU - request.user = mixer.blend(User) - - response = login_required_view(request, cve_code="CVE-2010-3333") - assert response.status_code == 200 - -# def test_login_required_view_unauthenticated(factory, model_object, db): #db argument na koncu -def test_login_required_view_unauthenticated(factory, my_model_object): - # mixer.blend('moje_api.NazwaModelu') # #jakby testowac z bazy - path = reverse('login_required_view', kwargs={"cve_code": "CVE-2010-3333"}) - request = factory.get(path) # TUUUUUUUU - request.user = AnonymousUser() - - response = login_required_view(request, cve_code="CVE-2010-3333") - assert response.status_code == 302 - # assert 'accounts/login' in response.url \ No newline at end of file diff --git a/sarenka/backend/api_searcher/third_services/cve_circl/wrappers/cve_parser.py b/sarenka/backend/api_searcher/third_services/cve_circl/wrappers/cve_parser.py index 990d2d97..847e4175 100644 --- a/sarenka/backend/api_searcher/third_services/cve_circl/wrappers/cve_parser.py +++ b/sarenka/backend/api_searcher/third_services/cve_circl/wrappers/cve_parser.py @@ -48,7 +48,6 @@ def products(data) -> List[ProductWrapper]: Więcej informacji: https://csrc.nist.gov/projects/security-content-automation-protocol/specifications/cpe """ data = data["vulnerable_product"] - # print(data) vendor_idx = 3 name_idx = 4 diff --git a/sarenka/backend/api_searcher/urls.py b/sarenka/backend/api_searcher/urls.py index 3d945db4..7fe085ab 100644 --- a/sarenka/backend/api_searcher/urls.py +++ b/sarenka/backend/api_searcher/urls.py @@ -1,8 +1,7 @@ from django.urls import path from .views.search_engines import (CensysHostSearchView, ShodanHostSearchView, - login_required_view, - SearcherView) + SearcherFullView) from .views.vendor_list import VendorListView from .views.dns import DNSSearcherView @@ -17,9 +16,7 @@ path('censys/', CensysHostSearchView.as_view(), name="get_censys_host_data"), path('shodan/', ShodanHostSearchView.as_view(), name="get_shodan_host_data"), - path('search/', SearcherView.as_view(), name="search"), - - path('login_required_view/', login_required_view, name="login_required_view"), + path('search/', SearcherFullView.as_view(), name="search"), path("dns/", DNSSearcherView.as_view(), name="dns_record"), diff --git a/sarenka/backend/api_searcher/views/search_engines.py b/sarenka/backend/api_searcher/views/search_engines.py index ab474d02..92988176 100644 --- a/sarenka/backend/api_searcher/views/search_engines.py +++ b/sarenka/backend/api_searcher/views/search_engines.py @@ -14,6 +14,16 @@ logger = logging.getLogger('django') +class LocalUrlCreator: + """Klasa pomocnicza generujaca urle do wnętrza aplikacji.""" + @staticmethod + def get_user_credentials_url(request): + return Common(request).host_address + reverse("user_credentials") + + + + + class CensysHostSearchView(views.APIView): """ Widok Django zwracający dane z serwisu http://censys.io/. @@ -25,18 +35,18 @@ def get(self, request, ip_address): :param request: obiekt request dla widoku Django :return: dane w postaci json zawierajace informacje o hoście zwrócone przez serwis https://censys.io/. """ + settings_url = LocalUrlCreator.get_user_credentials_url(request) + try: user_credentials = UserCredentials() response = CensysHostSearch(user_credentials).get_data(ip_address) return Response(response) except CensysHostSearchError as ex: - host_address = Common(request).host_address - settings_url = host_address + reverse('settings') - return Response({"message": f"Please create account on https://censys.io/ service and add valid credentials " + return Response({"error": f"Please create account on https://censys.io/ service and add valid credentials " f"for SARENKA app on {settings_url}", "details": str(ex)}, status=status.HTTP_401_UNAUTHORIZED) except Exception as ex: - return Response({"message": "Unable to get infromation from https://censys.io/ service.", + return Response({"error": f"Unable to get infromation from https://censys.io/ service.", "details": str(ex)}, status=status.HTTP_400_BAD_REQUEST) @@ -50,21 +60,22 @@ def get(self, request, ip_address): :param request: obiekt request dla widoku Django :return: dane w postaci json zawierajace informacje o hoście zwrócone przez serwis https://censys.io/. """ + settings_url = LocalUrlCreator.get_user_credentials_url(request) + try: user_credentials = UserCredentials() response = ShodanHostSearch(user_credentials).get_data(ip_address) return Response({"shodan": response}) except ShodanHostSearchError as ex: - host_address = Common(request).host_address - settings_url = host_address + reverse('settings') - return Response({"message": f"Please create account on https://www.shodan.io/ service and add valid credentials " + return Response({"error": f"Please create account on https://www.shodan.io/ service and add valid credentials " f"for SARENKA app on {settings_url}", "details": str(ex)}, status=status.HTTP_401_UNAUTHORIZED) except Exception as ex: - return Response({"message": "Unable to get infromation from https://www.shodan.io/ service.", + return Response({"error": f"Unable to get infromation from https://www.shodan.io/ service.", "details": str(ex)}, status=status.HTTP_400_BAD_REQUEST) -class SearcherView(views.APIView): + +class SearcherFullView(views.APIView): """ Widok Django zwracajacy wszystkie dane dla hostu podanego przez użytkownika. Zawiera dane ze wszsytkich serwisów trzecich, informacje o DNS oraz banner. diff --git a/sarenka/backend/api_searcher/views/user_credentials.py b/sarenka/backend/api_searcher/views/user_credentials.py index 018bb3ed..fd7c79fe 100644 --- a/sarenka/backend/api_searcher/views/user_credentials.py +++ b/sarenka/backend/api_searcher/views/user_credentials.py @@ -1,7 +1,7 @@ from rest_framework import views, status from rest_framework.response import Response -from api_searcher.serializers import UserCredentialsSerializer +from api_searcher.serializers import UserCredentialsSerializer, CensysCredentailsSerializer, ShodanCredentailsSerializer from api_searcher.search_engines.user_credentials import UserCredentials, UserCredentialsError from api_searcher.search_engines.user_credentials_updater import UserCredentialsUpdater, UserCredentialsUpdaterError @@ -12,7 +12,6 @@ class UserCredentialsView(views.APIView): def get(self, request): try: user_credentials = UserCredentials() - print("user_credentials.censys.api_id: ", user_credentials.censys) details = { "censys": { "API_ID": user_credentials.censys.api_id, @@ -28,45 +27,33 @@ def get(self, request): except UserCredentialsError as ex: Response({"error": "Unable to get user credentials.", "details": str(ex)}, status.HTTP_400_BAD_REQUEST) except Exception as ex: - print(type(ex)) - print(ex) return Response({"error": "User credentials are not valid. Please chceck file " "user_credentials.sqlite3 database", "details": str(ex)}, status=status.HTTP_400_BAD_REQUEST) def post(self, request): - """Tworzy plik z danymi użytkownika do serwisów trzecich.""" - try: - - serializer = UserCredentialsSerializer(data=request.data) + """Zapisuje do bazy dane uwierzytelniające użytkownika do serwisów trzecich.""" - # walidacja danych - if serializer.is_valid(): - user_credentials = { - "censys": { - "API_ID": serializer.data.get("censys_API_ID"), - "Secret": serializer.data.get("censys_Secret"), - }, - "shodan": { - "user": serializer.data.get("shodan_user"), - "api_key": serializer.data.get("shodan_api_key"), - } + user_data = request.data + print(user_data) + try: + details = { + "censys": { + "API_ID": user_data.get("censys.api_id"), + "Secret": user_data.get("censys.secret"), + }, + "shodan": { + "user": user_data.get("shodan.user"), + "api_key": user_data.get("shodan.api_key"), } - UserCredentialsUpdater(user_credentials).update() - return Response({"message": "User credentials added.", "details": user_credentials}) - else: - return Response({"message": "User credentials are not valid.", "details": serializer.errors}, - status=status.HTTP_400_BAD_REQUEST) - - except UserCredentialsUpdaterError as ex: - return Response({"error": "Unable to update user credentials.", "details": str(ex)}, - status=status.HTTP_400_BAD_REQUEST) + } + UserCredentialsUpdater(details).update() + return Response({"message": "User credentials added.", "details": details}) + except UserCredentialsError as ex: + Response({"error": "Invalid user credentials.", "details": str(ex)}, status.HTTP_400_BAD_REQUEST) except Exception as ex: - return Response({"error": "User credentials are not valid. Please chceck if file " - "user_credentials.sqlite3 exists " - "and is valid or copy from repository https://github.com/pawlaczyk/sarenka/", - "details": str(ex)}, - status=status.HTTP_400_BAD_REQUEST) + return Response({"error": "Unable to add user credentials.", "details": str(ex)}, status.HTTP_400_BAD_REQUEST) + diff --git a/sarenka/backend/api_vulnerabilities/cve_and_cwe/general_scraper.py b/sarenka/backend/api_vulnerabilities/cve_and_cwe/general_scraper.py index 75b2c2dd..911dda39 100644 --- a/sarenka/backend/api_vulnerabilities/cve_and_cwe/general_scraper.py +++ b/sarenka/backend/api_vulnerabilities/cve_and_cwe/general_scraper.py @@ -75,10 +75,3 @@ def get_keywords(self) -> Optional[str]: def get_image(self): og_image = self.soup.find("meta", property="og:image") return og_image.get('content') if og_image else None - - -if __name__ == "__main__": - try: - GeneralScraper("https://www.yaheeeeeeeeeeeeeeeeeeeeeeoo.com") - except: - print("obsluzone elo") \ No newline at end of file diff --git a/sarenka/backend/api_vulnerabilities/cve_and_cwe/nist_cve_scrapers.py b/sarenka/backend/api_vulnerabilities/cve_and_cwe/nist_cve_scrapers.py index fb575537..dec885a3 100644 --- a/sarenka/backend/api_vulnerabilities/cve_and_cwe/nist_cve_scrapers.py +++ b/sarenka/backend/api_vulnerabilities/cve_and_cwe/nist_cve_scrapers.py @@ -165,10 +165,3 @@ def get_data(self): "vulnerability_source" : self.get_vuln_source(soup) } - -if __name__ == "__main__": - # nist_cve_scraper = NISTCVEScraper("CVE-2019-4570") - # nist_cve_scraper = NISTCVEScraper("CVE-2014-8958") - nist_cve_scraper = NISTCVEScraper("CVE-2009-1532") - # print(nist_cve_scraper.get_data()) - # nist_cve_scraper.get_data() diff --git a/sarenka/backend/backend/settings/base.py b/sarenka/backend/backend/settings/base.py index 43c66422..c2aac435 100644 --- a/sarenka/backend/backend/settings/base.py +++ b/sarenka/backend/backend/settings/base.py @@ -6,6 +6,9 @@ os.path.dirname(os.path.abspath(__file__)) ) +USER_CREDENTIALS_DB_NAME = "user_credentials" +USER_CREDENTIALS_DB_FILE = "user_credentials.sqlite3" + # SECRET_KEY = config('SECRET_KEY') SECRET_KEY = "^oa#2a*y#rr-vhoi0m&s4+ph&m5^=iq-7wdiitm1@12p15z151" @@ -144,9 +147,9 @@ 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, - 'user_credentials': { + USER_CREDENTIALS_DB_NAME: { 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'user_credentials.sqlite3'), + 'NAME': os.path.join(BASE_DIR, USER_CREDENTIALS_DB_FILE), }, 'CWE_NONE': { 'ENGINE': 'django.db.backends.sqlite3', diff --git a/sarenka/backend/backend/user_credentials.sqlite3 b/sarenka/backend/backend/user_credentials.sqlite3 index 5d2b2ab2..e40af252 100644 Binary files a/sarenka/backend/backend/user_credentials.sqlite3 and b/sarenka/backend/backend/user_credentials.sqlite3 differ diff --git a/sarenka/backend/common/dict_x.py b/sarenka/backend/common/dict_x.py index f507868e..1a76aefb 100644 --- a/sarenka/backend/common/dict_x.py +++ b/sarenka/backend/common/dict_x.py @@ -19,22 +19,3 @@ def __delattr__(self, key): def __repr__(self): return '' - - -if __name__ == "__main__": - data = DictX({ - "name": "bo" - }) - - # use dot to get - print(data.name) - print(data["name"]) - - # use dot to set - data.state = "NY" - print(data.state) - print(data["state"]) - - # use dot to delete - del data.state - print(data) \ No newline at end of file diff --git a/sarenka/backend/common/text_parser.py b/sarenka/backend/common/text_parser.py index 140a1f01..02ce618d 100644 --- a/sarenka/backend/common/text_parser.py +++ b/sarenka/backend/common/text_parser.py @@ -39,9 +39,7 @@ def get_cve_list_from_file(filename): except BaseException as ex: not_analyzed.append(line) # TODO: logger - flat_list = Common.list_flattening(matches) - print("NOT ANALYZED: ", not_analyzed) return TextParser.get_unique_list(flat_list) diff --git a/urls_local.txt b/urls_local.txt index 7a5c8b59..821bebb3 100644 --- a/urls_local.txt +++ b/urls_local.txt @@ -17,10 +17,10 @@ http://127.0.0.1:8000/api/local/registry ### CWE #lista wszystkich CWE bez szczegółów -http://127.0.0.1:8000/api/cwe/all +http://127.0.0.1:8000/vulns/cwe/all # lista wszystkich CWE (ze stronnicowaniem) -http://127.0.0.1:8000/vulns/cwe/all +http://127.0.0.1:8000/vulns/cwe/all/5 http://127.0.0.1:8000/vulns/cwe/all/-1 # obsłużonmyb blad # pojedynczy cwe @@ -64,5 +64,13 @@ http://127.0.0.1:8000/api/censys/ewrwe # obsłużone # lista dostawców http://127.0.0.1:8000/api/list_vendors #wymaga credentiali -# settings -http://127.0.0.1:8000/api/user_credentials \ No newline at end of file +# user credentials +http://127.0.0.1:8000/api/user_credentials + +http://127.0.0.1:8000/api/user_credentials POST +{ + "censys.api_id": "1postman", + "censys.secret" : "2postman", + "shodan.user": "3postman", + "shodan.api_key": "4postman" +} \ No newline at end of file