diff --git a/main.py b/main.py index 45737bb..573e290 100644 --- a/main.py +++ b/main.py @@ -29,9 +29,11 @@ from views.view_alert import alert from views.view_startover import startover + # Import REST API Endpoints from views_api.api_health import Health from views_api.api_scan import Scan +from views_api.api_update import Update app = Flask(__name__) @@ -56,6 +58,7 @@ app.register_blueprint(alert) app.register_blueprint(startover) + app.config.update( SESSION_COOKIE_SAMESITE='Strict', ) @@ -63,7 +66,8 @@ api = Api(app) api.add_resource(Health, '/health') -api.add_resource(Scan, '/api/scan', '/api/scan/') +api.add_resource(Update, '/api/update', '/api/update/') +api.add_resource(Scan, '/api/scan', '/api/scan/') # Set Security Headers diff --git a/templates/documentation.html b/templates/documentation.html index 3ccf54a..b6b89ba 100644 --- a/templates/documentation.html +++ b/templates/documentation.html @@ -84,6 +84,7 @@

Graphical User Interface

  • 3. Reports
  • 4. Notifications
  • 5. Security
  • +
  • 6. Upgrade
  • @@ -285,6 +286,16 @@

    Security

  • Cookie Protection - Cookie security flags are used, such as SameSite, HttpOnly, etc.
  • If you identify a security vulnerability, please submit a bug to us on GitHub. +
    +

    Upgrade

    +

    If you want to upgrade your platform, the fastest way is to simply git clone and overwrite all the files while keeping key files such as configurations.

    +
      +
    1. Make a copy of config.py if you wish to save your configurations
    2. +
    3. Remove /opt/nerve and git clone it again from: GitHub
    4. +
    5. Move config.py file back into /opt/nerve
    6. +
    7. Restart the service using systemctl restart nerve
    8. +
    +

    You could set up a cron task auto-upgrade NERVE, there's an API endpoint to check whether you have the latest version or not that you could use for this purpose: /api/update/platform

    @@ -314,6 +325,7 @@

    API

  • POST /api/scan
  • GET /api/scan/status
  • PUT /api/scan/reset
  • +
  • GET /api/update/platform
  • @@ -336,12 +348,13 @@

    API

    API Authentication is done using Basic Authentication.

    Basic authentication is a simple authentication scheme built into the HTTP protocol. The client sends HTTP requests with the Authorization header that contains the word Basic word followed by a space and a base64-encoded string username:password

    With each API call, you need to pass the credentials. Here is an example with Python and the requests library:

    -
    -
    import requests
    +
    +import requests
     from requests.auth import HTTPBasicAuth
     username = 'admin'
     password = 'admin'
    -requests.post('https://172.21.50.1/scan', auth=HTTPBasicAuth(username, password), json={'key':'value'})
    +requests.post('https://172.21.50.1/scan', auth=HTTPBasicAuth(username, password), json={'key':'value'}) +
    @@ -391,6 +404,13 @@

    API

    True Resets the Server. Roll Back. + + 5. + GET + /api/update/platform + True + Checks if updates are available + @@ -407,10 +427,10 @@

    API

    GET /health
    -    import requests
    -    >>> requests.get('http://endpoint/health')
    +import requests
    +>>> requests.get('http://endpoint/health')
     
    -    <<< {'status': 'OK'}
    +<<< {'status': 'OK'}
                                 
    @@ -423,46 +443,46 @@

    API

    POST /api/scan
    -      import requests
    -      from requests.auth import HTTPBasicAuth
    +import requests
    +from requests.auth import HTTPBasicAuth
     
    -      DEFAULT_SCAN = {
    -        'targets':{
    -          'networks':[],
    -          'excluded_networks':[],
    -          'domains':[]
    -        },
    -        'config':{
    -          'name':'Default',
    -          'description':'My Default Scan',
    -          'engineer':'Default',
    -          'allow_aggressive':3,
    -          'allow_dos':True,
    -          'allow_bf':True,
    -          'allow_internet':True,
    -          'dictionary':{
    -            'usernames':[],
    -            'passwords':[]
    -          },
    -          'scan_opts':{
    -            'interface':None,
    -            'max_ports':1000,
    -            'custom_ports':[],
    -            'parallel_scan':50,
    -            'parallel_attack':30,
    -          },
    -          'post_event':{
    -            'webhook':None
    -          },
    -          'frequency':'once'
    -        }
    -      }
    +DEFAULT_SCAN = {
    +  'targets':{
    +    'networks':[],
    +    'excluded_networks':[],
    +    'domains':[]
    +  },
    +  'config':{
    +    'name':'Default',
    +    'description':'My Default Scan',
    +    'engineer':'Default',
    +    'allow_aggressive':3,
    +    'allow_dos':True,
    +    'allow_bf':True,
    +    'allow_internet':True,
    +    'dictionary':{
    +      'usernames':[],
    +      'passwords':[]
    +    },
    +    'scan_opts':{
    +      'interface':None,
    +      'max_ports':1000,
    +      'custom_ports':[],
    +      'parallel_scan':50,
    +      'parallel_attack':30,
    +    },
    +    'post_event':{
    +      'webhook':None
    +    },
    +    'frequency':'once'
    +  }
    +}
     
     
     
    -      >>> requests.post('http://endpoint/api/scan', auth=HTTPBasicAuth("admin", "admin"), json=DEFAULT_SCAN)
    +>>> requests.post('http://endpoint/api/scan', auth=HTTPBasicAuth("admin", "admin"), json=DEFAULT_SCAN)
     
    -      <<< {'status': 'Registered a new scan successfully!'}
    +<<< {'status': 'Registered a new scan successfully!'}
                                   
    @@ -475,19 +495,19 @@

    API

    GET /api/scan/status
    -          import requests
    -          from requests.auth import HTTPBasicAuth
    +import requests
    +from requests.auth import HTTPBasicAuth
     
    -          >>> resp = requests.get('http://endpoint/api/scan/status', auth=HTTPBasicAuth("admin", "admin"))
    +>>> resp = requests.get('http://endpoint/api/scan/status', auth=HTTPBasicAuth("admin", "admin"))
     
    -          <<<
    -          .. snip .. 
    -          {'scan_config': {'metadata': {'issuer': {'source_ip': '127.0.0.1'},
    -                              'timestamp': '2020-07-29 00:19:27',
    -                              'unique_id': '6eddab7b'},
    -                          'status': 'incomplete',
    -                          'vulnerabilities': {}}
    -          .. snip ..    
    +<<<
    +.. snip .. 
    +{'scan_config': {'metadata': {'issuer': {'source_ip': '127.0.0.1'},
    +                    'timestamp': '2020-07-29 00:19:27',
    +                    'unique_id': '6eddab7b'},
    +                'status': 'incomplete',
    +                'vulnerabilities': {}}
    +.. snip ..    
               
    @@ -502,18 +522,35 @@

    API

    PUT /api/scan/reset
    -            import requests
    -            from requests.auth import HTTPBasicAuth
    +import requests
    +from requests.auth import HTTPBasicAuth
     
    -            >>> requests.put('http://endpoint/api/scan/reset', auth=HTTPBasicAuth("admin", "admin"))
    +>>> requests.put('http://endpoint/api/scan/reset', auth=HTTPBasicAuth("admin", "admin"))
     
    -            <<< {'status': 'flushed scan state'}
    +<<< {'status': 'flushed scan state'}
                               
    +
    +
    +
    +
    GET /api/update/platform
    +
    +
    +import requests
    +from requests.auth import HTTPBasicAuth
    +
    +>>> requests.put('http://endpoint/api/update/platform', auth=HTTPBasicAuth("admin", "admin"))
    +
    +<<< {'status': 'system is up to date'}
    +                          
    +
    +
    +
    +
    diff --git a/views_api/api_update.py b/views_api/api_update.py new file mode 100644 index 0000000..b9f49bc --- /dev/null +++ b/views_api/api_update.py @@ -0,0 +1,23 @@ +import requests + +from core.utils import Utils +from core.security import auth + +from flask_restful import Resource +from flask import request + +class Update(Resource): + @auth.login_required + def get(self, component=None): + utils = Utils() + if not component: + return {'status':'Component is missing'}, 400 + + if component == 'platform': + if not utils.is_version_latest: + return {'status':'updates are available'} + else: + return {'status':'system is up to date'} + + + return {'status':'unsupported action'}, 400 \ No newline at end of file