├── shared
├── __init__.py
├── hashtools.py
├── console.py
├── mactools.py
├── mltools.py
├── dnstools.py
├── .gitignore
├── iftools.py
├── api_wrapper.py
└── iptools.py
├── core
├── shared
├── utils
│ ├── __init__.py
│ └── utils.py
├── data
│ ├── plugins
│ │ └── available
│ │ │ ├── freebox_detector
│ │ │ └── plugin.json
│ │ │ └── junos_version
│ │ │ └── plugin.json
│ └── nltk_data
│ │ └── tokenizers
│ │ └── punkt
│ │ └── PY3
│ │ └── english.pickle
├── config
│ ├── core.conf
│ ├── production.conf
│ └── docker.conf
├── core.conf
├── .dockerignore
├── config.py
├── database.py
└── api.py
├── sensor
├── shared
├── sensor.conf
├── setup.py
├── config.py
├── .gitignore
└── sensor.py
├── tools
├── shared
└── insert_fake_networks.py
├── web
├── test
│ ├── actions
│ │ └── .keep
│ ├── sources
│ │ └── .keep
│ ├── stores
│ │ └── .keep
│ ├── loadtests.js
│ ├── config
│ │ └── ConfigTest.js
│ ├── components
│ │ ├── MainTest.js
│ │ ├── HomeComponentTest.js
│ │ ├── GraphComponentTest.js
│ │ ├── HeaderComponentTest.js
│ │ ├── LoaderComponentTest.js
│ │ ├── NavbarComponentTest.js
│ │ ├── SideBarComponentTest.js
│ │ ├── home
│ │ │ └── MetricsComponentTest.js
│ │ ├── LoginPageComponentTest.js
│ │ ├── SearchBarComponentTest.js
│ │ ├── AddnetworkComponentTest.js
│ │ ├── BreadcrumbComponentTest.js
│ │ ├── HostDetailComponentTest.js
│ │ ├── SensorListComponentTest.js
│ │ ├── NetworkListComponentTest.js
│ │ ├── settings
│ │ │ ├── UserlistComponentTest.js
│ │ │ └── PluginlistComponentTest.js
│ │ ├── hostdetail
│ │ │ └── ServicesComponentTest.js
│ │ ├── modals
│ │ │ └── NewNetworkComponentTest.js
│ │ └── NetworkDetailComponentTest.js
│ └── helpers
│ │ └── shallowRenderHelper.js
├── .dockerignore
├── src
│ ├── styles
│ │ ├── Graph.css
│ │ ├── Home.css
│ │ ├── home
│ │ │ └── Metrics.css
│ │ ├── Breadcrumb.css
│ │ ├── settings
│ │ │ ├── SensorList.css
│ │ │ ├── Userlist.css
│ │ │ └── Pluginlist.css
│ │ ├── NetworkDetail.css
│ │ ├── hostdetail
│ │ │ └── Services.css
│ │ ├── Loader.css
│ │ ├── Search.css
│ │ ├── LoginPage.css
│ │ ├── NetworkList.css
│ │ ├── SideBar.css
│ │ ├── Navbar.css
│ │ ├── HostDetail.css
│ │ └── App.css
│ ├── favicon.ico
│ ├── images
│ │ └── logo.png
│ ├── config
│ │ ├── base.js
│ │ ├── dev.js
│ │ ├── dist.js
│ │ ├── test.js
│ │ └── README.md
│ ├── index.html
│ ├── lib
│ │ ├── tools.js
│ │ └── api.js
│ ├── components
│ │ ├── LoaderComponent.js
│ │ ├── HeaderComponent.js
│ │ ├── SidebarComponent.js
│ │ ├── hostdetail
│ │ │ └── ServicesComponent.js
│ │ ├── settings
│ │ │ ├── UserlistComponent.js
│ │ │ ├── PluginlistComponent.js
│ │ │ └── SensorListComponent.js
│ │ ├── SearchComponent.js
│ │ ├── GraphComponent.js
│ │ ├── HomeComponent.js
│ │ ├── home
│ │ │ └── MetricsComponent.js
│ │ ├── BreadcrumbComponent.js
│ │ ├── NetworkDetailComponent.js
│ │ ├── HostDetailComponent.js
│ │ ├── modals
│ │ │ └── NewNetworkComponent.js
│ │ ├── LoginPageComponent.js
│ │ ├── NetworkListComponent.js
│ │ └── NavbarComponent.js
│ ├── reducers
│ │ ├── graph.js
│ │ ├── users.js
│ │ ├── metrics.js
│ │ ├── plugins.js
│ │ ├── sensors.js
│ │ ├── search.js
│ │ ├── hosts.js
│ │ ├── sessions.js
│ │ ├── networks.js
│ │ └── index.js
│ ├── stores
│ │ └── configureStore.js
│ ├── actions
│ │ ├── graph.js
│ │ ├── users.js
│ │ ├── metrics.js
│ │ ├── plugins.js
│ │ ├── sensors.js
│ │ ├── search.js
│ │ ├── hosts.js
│ │ ├── networks.js
│ │ └── sessions.js
│ ├── index.js
│ ├── routes.js
│ └── containers
│ │ ├── SearchContainer.js
│ │ ├── HomeContainer.js
│ │ ├── NetworkListContainer.js
│ │ ├── HostDetailContainer.js
│ │ ├── NetworkDetailContainer.js
│ │ ├── MainContainer.js
│ │ └── SettingsContainer.js
├── .yo-rc.json
├── .babelrc
├── .editorconfig
├── .gitignore
├── .eslintrc
├── karma.conf.js
├── webpack.config.js
├── cfg
│ ├── dev.js
│ ├── dist.js
│ ├── base.js
│ ├── test.js
│ └── defaults.js
├── server.js
└── package.json
├── _config.yml
├── .gitattributes
├── doc
└── stack.png
├── docker
├── dockerfile-web
├── docker-compose.yml
├── dockerfile-core
└── docker-compose-all.yml
├── requirements.txt
├── LICENSE
├── .travis.yml
├── .gitignore
└── README.md
/shared/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/shared:
--------------------------------------------------------------------------------
1 | ../shared/
--------------------------------------------------------------------------------
/core/utils/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sensor/shared:
--------------------------------------------------------------------------------
1 | ../shared/
--------------------------------------------------------------------------------
/tools/shared:
--------------------------------------------------------------------------------
1 | ../shared/
--------------------------------------------------------------------------------
/web/test/actions/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/test/sources/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/test/stores/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-midnight
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | web/test/* linguist-vendored
2 |
--------------------------------------------------------------------------------
/web/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | dist
4 |
--------------------------------------------------------------------------------
/web/src/styles/Graph.css:
--------------------------------------------------------------------------------
1 | .graph-component {
2 | }
3 |
--------------------------------------------------------------------------------
/web/src/styles/Home.css:
--------------------------------------------------------------------------------
1 | .home-component {
2 | }
3 |
--------------------------------------------------------------------------------
/web/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-react-webpack": {}
3 | }
--------------------------------------------------------------------------------
/web/src/styles/home/Metrics.css:
--------------------------------------------------------------------------------
1 | .metrics-component {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/doc/stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gbnk0/nmt/HEAD/doc/stack.png
--------------------------------------------------------------------------------
/web/src/styles/Breadcrumb.css:
--------------------------------------------------------------------------------
1 | .breadcrumb-component {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/web/src/styles/settings/SensorList.css:
--------------------------------------------------------------------------------
1 | .sensorlist-component {
2 | }
3 |
--------------------------------------------------------------------------------
/web/src/styles/NetworkDetail.css:
--------------------------------------------------------------------------------
1 | .networkdetail-component {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/web/src/styles/hostdetail/Services.css:
--------------------------------------------------------------------------------
1 | .services-component {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/web/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gbnk0/nmt/HEAD/web/src/favicon.ico
--------------------------------------------------------------------------------
/web/src/styles/Loader.css:
--------------------------------------------------------------------------------
1 | .loader-component {
2 | /*padding-top: 20px;*/
3 | }
4 |
--------------------------------------------------------------------------------
/web/src/styles/Search.css:
--------------------------------------------------------------------------------
1 | .search-component {
2 | /* padding-top: 20px;*/
3 | }
4 |
--------------------------------------------------------------------------------
/web/src/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gbnk0/nmt/HEAD/web/src/images/logo.png
--------------------------------------------------------------------------------
/web/src/styles/LoginPage.css:
--------------------------------------------------------------------------------
1 | .loginpage-component {
2 | padding-top: 10%;
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/web/src/styles/NetworkList.css:
--------------------------------------------------------------------------------
1 | .networklist-component {
2 | /* padding-top: 20px;*/
3 | }
4 |
--------------------------------------------------------------------------------
/web/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "stage-0",
5 | "react"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/web/src/styles/settings/Userlist.css:
--------------------------------------------------------------------------------
1 | .userlist-component {
2 | /* border: 1px dashed #f00; */
3 | }
4 |
--------------------------------------------------------------------------------
/web/src/styles/settings/Pluginlist.css:
--------------------------------------------------------------------------------
1 | .pluginlist-component {
2 | /* border: 1px dashed #f00; */
3 | }
4 |
--------------------------------------------------------------------------------
/core/data/plugins/available/freebox_detector/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.1",
3 | "name": "Freebox detector"
4 | }
5 |
--------------------------------------------------------------------------------
/core/data/plugins/available/junos_version/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.1",
3 | "name": "Junos version detector"
4 | }
5 |
--------------------------------------------------------------------------------
/shared/hashtools.py:
--------------------------------------------------------------------------------
1 | import shortuuid
2 |
3 |
4 | def make_shortuuid(string):
5 | return shortuuid.uuid(name=string)
6 |
--------------------------------------------------------------------------------
/web/src/config/base.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Settings configured here will be merged into the final config object.
4 | export default {
5 | }
6 |
--------------------------------------------------------------------------------
/core/data/nltk_data/tokenizers/punkt/PY3/english.pickle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gbnk0/nmt/HEAD/core/data/nltk_data/tokenizers/punkt/PY3/english.pickle
--------------------------------------------------------------------------------
/core/config/core.conf:
--------------------------------------------------------------------------------
1 | [general]
2 | production = 0
3 |
4 | [api]
5 | debug = 1
6 |
7 | [database]
8 | host = localhost
9 | port = 28015
10 | name = nmt
--------------------------------------------------------------------------------
/core/core.conf:
--------------------------------------------------------------------------------
1 | [general]
2 | production = 0
3 |
4 | [api]
5 | debug = 1
6 |
7 | [database]
8 | host = localhost
9 | port = 28015
10 | name = nmt
11 |
--------------------------------------------------------------------------------
/core/config/production.conf:
--------------------------------------------------------------------------------
1 | [general]
2 | production = 1
3 |
4 | [api]
5 | debug = 0
6 |
7 | [database]
8 | host = localhost
9 | port = 28015
10 | name = nmt
--------------------------------------------------------------------------------
/core/config/docker.conf:
--------------------------------------------------------------------------------
1 | [general]
2 | production = 0
3 |
4 | [api]
5 | debug = 1
6 |
7 | [database]
8 | host = rethinkdb
9 | port = 28015
10 | name = nmt
11 |
--------------------------------------------------------------------------------
/sensor/sensor.conf:
--------------------------------------------------------------------------------
1 | [general]
2 | sensor_name = sensor1
3 |
4 | [scanner]
5 | tcp_port_range = 1-1024
6 |
7 | [core_api]
8 | host = localhost
9 | port = 8080
10 |
--------------------------------------------------------------------------------
/web/src/styles/SideBar.css:
--------------------------------------------------------------------------------
1 | .sidebar-component {
2 | width: 100%;
3 | }
4 |
5 | .sidebar-component .ui .vertical menu {
6 | padding-right: 0px;
7 | width: 100%;
8 | margin-right: 0px;
9 | }
10 |
--------------------------------------------------------------------------------
/core/.dockerignore:
--------------------------------------------------------------------------------
1 | docker/
2 | docker-compose.yml
3 | Dockerfile
4 | dockerfile-core
5 | dockerfile-web
6 | .dockerignore
7 | .gitignore
8 | README.md
9 |
10 |
11 | */build
12 | */node_modules
13 |
14 |
--------------------------------------------------------------------------------
/docker/dockerfile-web:
--------------------------------------------------------------------------------
1 | FROM node:8
2 | ENV NPM_CONFIG_LOGLEVEL warn
3 | COPY web/ /
4 |
5 | RUN npm install
6 | RUN npm install -g webpack
7 | RUN npm install -g serve
8 | RUN npm run dist
9 |
10 | CMD serve -s dist -p 8000
11 | EXPOSE 8000
--------------------------------------------------------------------------------
/web/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/web/src/config/dev.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import baseConfig from './base';
4 |
5 | let config = {
6 | appEnv: 'dev', // feel free to remove the appEnv property her
7 | };
8 |
9 | export default Object.freeze(Object.assign({}, baseConfig, config));
10 |
--------------------------------------------------------------------------------
/web/src/config/dist.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import baseConfig from './base';
4 |
5 | let config = {
6 | appEnv: 'dist', // feel free to remove the appEnv property here
7 | };
8 |
9 | export default Object.freeze(Object.assign({}, baseConfig, config));
10 |
--------------------------------------------------------------------------------
/web/test/loadtests.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('babel-polyfill');
4 | require('core-js/fn/object/assign');
5 |
6 | // Add support for all files in the test directory
7 | const testsContext = require.context('.', true, /(Test\.js$)|(Helper\.js$)/);
8 | testsContext.keys().forEach(testsContext);
9 |
--------------------------------------------------------------------------------
/web/src/styles/Navbar.css:
--------------------------------------------------------------------------------
1 | .navbar-component {
2 | /*padding-top: 5px;*/
3 | padding-bottom: 50px;
4 |
5 | }
6 |
7 | .navbar-component .ui .menu {
8 | border-radius: 0;
9 | }
10 |
11 | .noradius {
12 | border-radius: 0;
13 | }
14 |
15 | .navbar-header {
16 | min-width: 12.85%;
17 | }
18 |
--------------------------------------------------------------------------------
/web/src/config/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import baseConfig from './base';
4 |
5 | let config = {
6 | appEnv: 'test',
7 | output: {
8 | publicPath: 'http://localhost:8000/'
9 | }
10 | // don't remove the appEnv property here
11 | };
12 |
13 | export default Object.freeze(Object.assign(baseConfig, config));
14 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Sanic_Cors==0.6.0.2
2 | attrdict==2.0.0
3 | netifaces==0.10.6
4 | nltk==3.2.5
5 | python-nmap
6 | python_json_logger==0.1.8
7 | requests==2.18.4
8 | rethinkdb==2.3.0.post6
9 | sanic==0.7.0
10 | sanic_openapi==0.4.0
11 | scapy_python3==0.21
12 | shortuuid==0.5.0
13 | termcolor==1.1.0
14 | textblob==0.13.0
15 | sanic_jwt
16 |
--------------------------------------------------------------------------------
/docker/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | rethinkdb:
4 | image: "rethinkdb:latest"
5 | ports:
6 | - "28015:28015"
7 | - "9090:8080"
8 | volumes:
9 | - RethinkDATA:/data
10 | networks:
11 | - core_net
12 |
13 | volumes:
14 | RethinkDATA:
15 | driver: local
16 |
17 | networks:
18 | core_net:
19 |
--------------------------------------------------------------------------------
/web/test/config/ConfigTest.js:
--------------------------------------------------------------------------------
1 | /*eslint-env node, mocha */
2 | /*global expect */
3 | /*eslint no-console: 0*/
4 | 'use strict';
5 |
6 | import config from 'config';
7 |
8 | describe('appEnvConfigTests', function () {
9 | it('should load app config file depending on current --env', function () {
10 | expect(config.appEnv).to.equal('test');
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/docker/dockerfile-core:
--------------------------------------------------------------------------------
1 | FROM python:3.6
2 | RUN apt-get update -y
3 | RUN apt-get install -y python3-pip
4 | COPY /core/ /app/core/
5 | COPY /requirements.txt /app/core/requirements.txt
6 | COPY /shared/ /app/shared/
7 | COPY /core/config/docker.conf /app/core/core.conf
8 | WORKDIR /app/core/
9 | RUN pip3 install -r requirements.txt
10 | CMD ["python3.6", "api.py"]
11 |
12 |
--------------------------------------------------------------------------------
/web/src/styles/HostDetail.css:
--------------------------------------------------------------------------------
1 | .hostdetail-component {
2 | }
3 |
4 | .card-h {
5 | box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
6 | transition: all 0.3s cubic-bezier(.25,.8,.25,1);
7 | border-top: 1px;
8 | border-top-color: chocolate;
9 | }
10 |
11 | .card-h:hover {
12 | box-shadow: 0 3px 5px rgba(0,0,0,0.25), 0 5px 5px rgba(0,0,0,0.22);
13 | }
--------------------------------------------------------------------------------
/sensor/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 | from setuptools import setup
3 |
4 | __author__ = 'NMT'
5 |
6 | setup(
7 | name='nmt-sensor',
8 | version='0.0.1',
9 | scripts=['sensor.py'],
10 | packages=['.', 'shared'],
11 | data_files=[("/etc/nmt/",
12 | ["sensor.conf"])],
13 | entry_points={
14 | 'console_scripts': [
15 | 'sensor = sensor:main',
16 | ],
17 | }
18 | )
--------------------------------------------------------------------------------
/core/config.py:
--------------------------------------------------------------------------------
1 | import configparser
2 |
3 | """ Config part """
4 | config = configparser.ConfigParser()
5 | config.read('core.conf')
6 |
7 |
8 | """ GENERAL CONFIG """
9 |
10 |
11 | production = bool(int(config['general']['production']))
12 |
13 |
14 | """ DATABASE CONFIG """
15 |
16 |
17 | db_host = config['database']['host']
18 | db_port = int(config['database']['port'])
19 | db_name = config['database']['name']
20 |
21 |
22 | """ API CONFIG """
23 |
24 | debug = bool(int(config['api']['debug']))
--------------------------------------------------------------------------------
/web/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NMT - Network Mapper Tool
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/web/src/lib/tools.js:
--------------------------------------------------------------------------------
1 | import Moment from 'moment';
2 |
3 | var Address4 = require('ip-address').Address4;
4 |
5 | export function formatDate(date,locale) {
6 | Moment.locale(locale);
7 | return Moment(date).format('D MMM YYYY H:m')
8 | }
9 |
10 | export function ValidateIPaddress(ipaddress)
11 | {
12 | var address = new Address4(ipaddress);
13 | return(address.isValid())
14 | }
15 |
16 | export function renderOs(host) {
17 | var os = '-'
18 | if ('details' in host) {
19 | if ('osfamily' in host.details) {
20 | var os = host.details.osfamily + ' ' + host.details.osgen;
21 | }
22 | }
23 | return os;
24 | }
--------------------------------------------------------------------------------
/shared/console.py:
--------------------------------------------------------------------------------
1 | from termcolor import colored
2 |
3 |
4 | def write(msg, status):
5 |
6 | if status == 0:
7 | color = 'green'
8 | sts = 'OK'
9 | elif status == 1:
10 | color = 'yellow'
11 | sts = 'WARN'
12 | elif status == 2:
13 | color = 'red'
14 | sts = 'FAIL'
15 | elif status == 3:
16 | color = 'white'
17 | sts = '*'
18 | elif status == 4:
19 | color = 'green'
20 | sts = '->'
21 |
22 | text = colored(sts, color)
23 | result = "[" + text + "] " + msg
24 |
25 | print(result)
26 |
27 | if __name__ == "__main__":
28 | write("Test message", 0)
29 |
--------------------------------------------------------------------------------
/web/test/components/MainTest.js:
--------------------------------------------------------------------------------
1 | /*eslint-env node, mocha */
2 | /*global expect */
3 | /*eslint no-console: 0*/
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import React from 'react/addons';
8 | // const TestUtils = React.addons.TestUtils;
9 | import createComponent from 'helpers/shallowRenderHelper';
10 |
11 | import Main from 'components/Main';
12 |
13 | describe('MainComponent', function () {
14 |
15 | beforeEach(function () {
16 | this.MainComponent = createComponent(Main);
17 | });
18 |
19 | it('should have its component name as default className', function () {
20 | expect(this.MainComponent.props.className).to.equal('index');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/HomeComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import HomeComponent from 'components//HomeComponent.js';
11 |
12 | describe('HomeComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(HomeComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('home-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/GraphComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import GraphComponent from 'components//GraphComponent.js';
11 |
12 | describe('GraphComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(GraphComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('graph-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/HeaderComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import HeaderComponent from 'components//HeaderComponent.js';
11 |
12 | describe('HeaderComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(HeaderComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('header-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/LoaderComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import LoaderComponent from 'components//LoaderComponent.js';
11 |
12 | describe('LoaderComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(LoaderComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('loader-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/NavbarComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import NavbarComponent from 'components//NavbarComponent.js';
11 |
12 | describe('NavbarComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(NavbarComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('navbar-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/SideBarComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import SideBarComponent from 'components//SideBarComponent.js';
11 |
12 | describe('SideBarComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(SideBarComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('sidebar-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/.gitignore:
--------------------------------------------------------------------------------
1 | dist/
2 | # Logs
3 | logs
4 | *.log
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
28 | node_modules
29 |
30 | # Bower
31 | bower_components/
32 |
33 | # IDE/Editor data
34 | .idea
35 |
--------------------------------------------------------------------------------
/web/test/components/home/MetricsComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import MetricsComponent from 'components/home/MetricsComponent.js';
11 |
12 | describe('MetricsComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(MetricsComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('metrics-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/LoginPageComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import LoginPageComponent from 'components//LoginPageComponent.js';
11 |
12 | describe('LoginPageComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(LoginPageComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('loginpage-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/SearchBarComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import SearchBarComponent from 'components//SearchBarComponent.js';
11 |
12 | describe('SearchBarComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(SearchBarComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('searchbar-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/AddnetworkComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import AddnetworkComponent from 'components//AddnetworkComponent.js';
11 |
12 | describe('AddnetworkComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(AddnetworkComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('addnetwork-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/BreadcrumbComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import BreadcrumbComponent from 'components//BreadcrumbComponent.js';
11 |
12 | describe('BreadcrumbComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(BreadcrumbComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('breadcrumb-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/HostDetailComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import HostDetailComponent from 'components//HostDetailComponent.js';
11 |
12 | describe('HostDetailComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(HostDetailComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('hostdetail-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/SensorListComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import SensorListComponent from 'components//SensorListComponent.js';
11 |
12 | describe('SensorListComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(SensorListComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('sensorlist-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/src/components/LoaderComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Dimmer, Loader, Segment } from 'semantic-ui-react'
5 |
6 | require('styles//Loader.css');
7 |
8 | class LoaderComponent extends React.Component {
9 | render() {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 |
24 | LoaderComponent.displayName = 'LoaderComponent';
25 |
26 | // Uncomment properties you need
27 | // LoaderComponent.propTypes = {};
28 | // LoaderComponent.defaultProps = {};
29 |
30 | export default LoaderComponent;
31 |
--------------------------------------------------------------------------------
/web/src/reducers/graph.js:
--------------------------------------------------------------------------------
1 | export function graphHasErrored(state = false, action) {
2 | switch (action.type) {
3 | case 'GRAPH_HAS_ERRORED':
4 | return action.hasErrored;
5 |
6 | default:
7 | return state;
8 | }
9 | }
10 |
11 | export function graphIsLoading(state = false, action) {
12 | switch (action.type) {
13 | case 'GRAPH_IS_LOADING':
14 | return action.isLoading;
15 |
16 | default:
17 | return state;
18 | }
19 | }
20 |
21 | export function graph(state = {}, action) {
22 | switch (action.type) {
23 | case 'GRAPH_FETCH_DATA_SUCCESS':
24 | return action.graph;
25 |
26 | default:
27 | return state;
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/web/src/reducers/users.js:
--------------------------------------------------------------------------------
1 | export function usersHasErrored(state = false, action) {
2 | switch (action.type) {
3 | case 'USERS_HAS_ERRORED':
4 | return action.hasErrored;
5 |
6 | default:
7 | return state;
8 | }
9 | }
10 |
11 | export function usersIsLoading(state = false, action) {
12 | switch (action.type) {
13 | case 'USERS_IS_LOADING':
14 | return action.isLoading;
15 |
16 | default:
17 | return state;
18 | }
19 | }
20 |
21 | export function users(state = {}, action) {
22 | switch (action.type) {
23 | case 'USERS_FETCH_DATA_SUCCESS':
24 | return action.users;
25 |
26 | default:
27 | return state;
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/web/test/components/NetworkListComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import NetworkListComponent from 'components//NetworkListComponent.js';
11 |
12 | describe('NetworkListComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(NetworkListComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('networklist-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/settings/UserlistComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import UserlistComponent from 'components/settings/UserlistComponent.js';
11 |
12 | describe('UserlistComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(UserlistComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('userlist-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/hostdetail/ServicesComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import ServicesComponent from 'components/hostdetail/ServicesComponent.js';
11 |
12 | describe('ServicesComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(ServicesComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('services-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/modals/NewNetworkComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import NewNetworkComponent from 'components/modals/NewNetworkComponent.js';
11 |
12 | describe('NewNetworkComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(NewNetworkComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('newnetwork-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/test/components/settings/PluginlistComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import PluginlistComponent from 'components/settings/PluginlistComponent.js';
11 |
12 | describe('PluginlistComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(PluginlistComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('pluginlist-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/src/reducers/metrics.js:
--------------------------------------------------------------------------------
1 | export function metricsHasErrored(state = false, action) {
2 | switch (action.type) {
3 | case 'METRICS_HAS_ERRORED':
4 | return action.hasErrored;
5 |
6 | default:
7 | return state;
8 | }
9 | }
10 |
11 | export function metricsIsLoading(state = false, action) {
12 | switch (action.type) {
13 | case 'METRICS_IS_LOADING':
14 | return action.isLoading;
15 |
16 | default:
17 | return state;
18 | }
19 | }
20 |
21 | export function metrics(state = {}, action) {
22 | switch (action.type) {
23 | case 'METRICS_FETCH_DATA_SUCCESS':
24 | return action.metrics;
25 |
26 | default:
27 | return state;
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/web/src/reducers/plugins.js:
--------------------------------------------------------------------------------
1 | export function pluginsHasErrored(state = false, action) {
2 | switch (action.type) {
3 | case 'PLUGINS_HAS_ERRORED':
4 | return action.hasErrored;
5 |
6 | default:
7 | return state;
8 | }
9 | }
10 |
11 | export function pluginsIsLoading(state = false, action) {
12 | switch (action.type) {
13 | case 'PLUGINS_IS_LOADING':
14 | return action.isLoading;
15 |
16 | default:
17 | return state;
18 | }
19 | }
20 |
21 | export function plugins(state = {}, action) {
22 | switch (action.type) {
23 | case 'PLUGINS_FETCH_DATA_SUCCESS':
24 | return action.plugins;
25 |
26 | default:
27 | return state;
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/web/src/reducers/sensors.js:
--------------------------------------------------------------------------------
1 | export function sensorsHasErrored(state = false, action) {
2 | switch (action.type) {
3 | case 'SENSORS_HAS_ERRORED':
4 | return action.hasErrored;
5 |
6 | default:
7 | return state;
8 | }
9 | }
10 |
11 | export function sensorsIsLoading(state = false, action) {
12 | switch (action.type) {
13 | case 'SENSORS_IS_LOADING':
14 | return action.isLoading;
15 |
16 | default:
17 | return state;
18 | }
19 | }
20 |
21 | export function sensors(state = {}, action) {
22 | switch (action.type) {
23 | case 'SENSORS_FETCH_DATA_SUCCESS':
24 | return action.sensors;
25 |
26 | default:
27 | return state;
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/web/test/components/NetworkDetailComponentTest.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, mocha */
2 | /* global expect */
3 | /* eslint no-console: 0 */
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import TestUtils from 'react-addons-test-utils';
8 | import createComponent from 'helpers/shallowRenderHelper';
9 |
10 | import NetworkDetailComponent from 'components//NetworkDetailComponent.js';
11 |
12 | describe('NetworkDetailComponent', () => {
13 | let component;
14 |
15 | beforeEach(() => {
16 | component = createComponent(NetworkDetailComponent);
17 | });
18 |
19 | it('should have its component name as default className', () => {
20 | expect(component.props.className).to.equal('networkdetail-component');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/web/src/stores/configureStore.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import rootReducer from '../reducers';
4 | import createHistory from 'history/createBrowserHistory'
5 | import { routerMiddleware} from 'react-router-redux'
6 |
7 | export const history = createHistory()
8 |
9 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
10 |
11 | const middleware = (
12 | composeEnhancers(
13 | applyMiddleware(thunk),
14 | applyMiddleware(routerMiddleware(history))
15 | )
16 | );
17 |
18 |
19 | export function configureStore(initialState) {
20 | return createStore(
21 | rootReducer,
22 | initialState,
23 | middleware
24 | );
25 | }
--------------------------------------------------------------------------------
/web/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "plugins": [
4 | "react"
5 | ],
6 | "parserOptions": {
7 | "ecmaVersion": 6,
8 | "sourceType": "module",
9 | "ecmaFeatures": {
10 | "jsx": true
11 | }
12 | },
13 | "env": {
14 | "browser": true,
15 | "amd": true,
16 | "es6": true,
17 | "node": true,
18 | "mocha": true
19 | },
20 | "rules": {
21 | "comma-dangle": 1,
22 | "quotes": [ 1, "single" ],
23 | "no-undef": 1,
24 | "global-strict": 0,
25 | "no-extra-semi": 1,
26 | "no-underscore-dangle": 0,
27 | "no-console": 1,
28 | "no-unused-vars": 1,
29 | "no-trailing-spaces": [1, { "skipBlankLines": true }],
30 | "no-unreachable": 1,
31 | "no-alert": 0,
32 | "react/jsx-uses-react": 1,
33 | "react/jsx-uses-vars": 1
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/web/src/components/HeaderComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Icon, Header, Segment } from 'semantic-ui-react';
5 |
6 | class HeaderComponent extends React.Component {
7 | render() {
8 | return (
9 |
10 |
11 |
12 |
13 | {this.props.isLoading ? 'LOADING...' :
14 | this.props.title}
15 |
16 | {this.props.subtitle}
17 |
18 |
19 |
20 |
21 |
22 | );
23 | }
24 | }
25 |
26 | HeaderComponent.displayName = 'HeaderComponent';
27 |
28 | export default HeaderComponent;
29 |
--------------------------------------------------------------------------------
/shared/mactools.py:
--------------------------------------------------------------------------------
1 |
2 | import pprint
3 | import requests
4 | import json
5 | import os
6 |
7 | datapath = os.path.abspath(__file__).split('/')[:-1]
8 | datapath = "/".join(datapath) + "/data/"
9 |
10 | macvendors = {}
11 | with open(datapath + 'mac_vendors.txt', 'r') as f:
12 | for line in f:
13 | line = line.strip().rstrip()
14 | line = line.split(';')
15 | mac = line[0]
16 | vendor = line[1]
17 | macvendors[mac] = vendor
18 |
19 |
20 | def get_mac_vendor(mac):
21 | result = "Unknown"
22 |
23 | # Anonymize mac
24 | mac = mac.split(':')[:3]
25 | mac = "".join(mac)
26 |
27 | mac = mac.strip(':')
28 | mac = mac.upper()
29 |
30 | if mac in macvendors.keys():
31 | result = macvendors[mac]
32 |
33 | return result
34 |
35 | if __name__ == "__main__":
36 | print(get_mac_vendor("4c:34:88"))
37 |
--------------------------------------------------------------------------------
/sensor/config.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import configparser
3 | from pythonjsonlogger import jsonlogger
4 |
5 | # For hiding scapy warnings
6 | logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
7 |
8 |
9 | """ Logging part """
10 | logger = logging.getLogger()
11 |
12 | logHandler = logging.StreamHandler()
13 | formatter = jsonlogger.JsonFormatter()
14 | logHandler.setFormatter(formatter)
15 | logger.addHandler(logHandler)
16 | logger.setLevel(logging.ERROR)
17 |
18 |
19 | """ Config part """
20 | config = configparser.ConfigParser()
21 | config.read('sensor.conf')
22 |
23 | api_host = config['core_api']['host']
24 | api_port = config['core_api']['port']
25 |
26 | sensor_name = config['general']['sensor_name']
27 |
28 | tcp_port_range = config['scanner']['tcp_port_range']
29 |
30 | api_url = "http://{}:{}".format(api_host, api_port)
31 |
32 | login = 'admin'
33 | password = 'password'
--------------------------------------------------------------------------------
/web/karma.conf.js:
--------------------------------------------------------------------------------
1 | var webpackCfg = require('./webpack.config');
2 |
3 | // Set node environment to testing
4 | process.env.NODE_ENV = 'test';
5 |
6 | module.exports = function(config) {
7 | config.set({
8 | basePath: '',
9 | browsers: [ 'PhantomJS' ],
10 | files: [
11 | 'test/loadtests.js'
12 | ],
13 | port: 8000,
14 | captureTimeout: 60000,
15 | frameworks: [ 'mocha', 'chai' ],
16 | client: {
17 | mocha: {}
18 | },
19 | singleRun: true,
20 | reporters: [ 'mocha', 'coverage' ],
21 | preprocessors: {
22 | 'test/loadtests.js': [ 'webpack', 'sourcemap' ]
23 | },
24 | webpack: webpackCfg,
25 | webpackServer: {
26 | noInfo: true
27 | },
28 | coverageReporter: {
29 | dir: 'coverage/',
30 | reporters: [
31 | { type: 'html' },
32 | { type: 'text' }
33 | ]
34 | }
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/web/src/styles/App.css:
--------------------------------------------------------------------------------
1 | /* Base Application Styles */
2 | body {
3 | background-color: #f2f2f2;
4 | overflow-y: scroll;
5 | }
6 |
7 |
8 | .index .notice {
9 | margin: 20px auto;
10 | padding: 15px 0;
11 | text-align: center;
12 | border: 1px solid #000;
13 | border-width: 1px 0;
14 | background: #666;
15 | }
16 |
17 |
18 |
19 | .tabs-menu {
20 | display: table;
21 | list-style: none;
22 | padding: 0;
23 | margin: 0;
24 | }
25 |
26 | .tabs-menu-item {
27 | float: left;
28 | margin-right: 20px;
29 | }
30 |
31 | .tabs-menu-item a {
32 | cursor: pointer;
33 | display: block;
34 | color: #A9A9A9;
35 | }
36 |
37 | .tabs-menu-item:not(.is-active) a:hover,
38 | .tabs-menu-item.is-active a {
39 | color: #3498DB;
40 | }
41 |
42 | .tab-panel {
43 | padding: 10px 50px;
44 | }
45 |
46 | .tabs-navigation {
47 | padding: 0 50px;
48 | }
49 |
50 | .tabs-panel {
51 | padding: 50px;
52 | }
--------------------------------------------------------------------------------
/web/src/config/README.md:
--------------------------------------------------------------------------------
1 | # About this folder
2 |
3 | This folder holds configuration files for different environments.
4 | You can use it to provide your app with different settings based on the
5 | current environment, e.g. to configure different API base urls depending on
6 | whether your setup runs in dev mode or is built for distribution.
7 | You can include the configuration into your code like this:
8 |
9 | **ES2015 Modules**
10 |
11 | ```js
12 | import config from 'config';
13 | ```
14 |
15 | **Common JS**
16 |
17 | Due to Babel6 we need to append `.default`.
18 |
19 | ```js
20 | let config = require('config').default;
21 | ```
22 |
23 | **Example**
24 |
25 | ```javascript
26 | import React from 'react';
27 | import config from 'config';
28 |
29 | class MyComponent extends React.Component {
30 | constructor(props, ctx) {
31 | super(props, ctx);
32 | let currentAppEnv = config.appEnv;
33 | }
34 | }
35 | ```
36 |
--------------------------------------------------------------------------------
/docker/docker-compose-all.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | rethinkdb:
4 | container_name: rethinkdb
5 | image: "rethinkdb:latest"
6 | ports:
7 | - "28015:28015"
8 | - "9090:8080"
9 | volumes:
10 | - RethinkDATA:/data
11 | networks:
12 | - core_net
13 |
14 | core_api:
15 | container_name: coreapi
16 | build:
17 | context: ../
18 | dockerfile: docker/dockerfile-core
19 | ports:
20 | - "8080:8080"
21 | networks:
22 | - core_net
23 | - front_net
24 | depends_on:
25 | - rethinkdb
26 |
27 | web:
28 | container_name: web
29 | build:
30 | context: ../
31 | dockerfile: docker/dockerfile-web
32 | ports:
33 | - "8000:8000"
34 | networks:
35 | - front_net
36 | depends_on:
37 | - core_api
38 | - rethinkdb
39 |
40 | volumes:
41 | RethinkDATA:
42 | driver: local
43 |
44 | networks:
45 | core_net:
46 | front_net:
47 |
--------------------------------------------------------------------------------
/web/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const args = require('minimist')(process.argv.slice(2));
5 |
6 | // List of allowed environments
7 | const allowedEnvs = ['dev', 'dist', 'test'];
8 |
9 | // Set the correct environment
10 | let env;
11 | if (args._.length > 0 && args._.indexOf('start') !== -1) {
12 | env = 'test';
13 | } else if (args.env) {
14 | env = args.env;
15 | } else {
16 | env = 'dev';
17 | }
18 | process.env.REACT_WEBPACK_ENV = env;
19 |
20 | /**
21 | * Build the webpack configuration
22 | * @param {String} wantedEnv The wanted environment
23 | * @return {Object} Webpack config
24 | */
25 | function buildConfig(wantedEnv) {
26 | let isValid = wantedEnv && wantedEnv.length > 0 && allowedEnvs.indexOf(wantedEnv) !== -1;
27 | let validEnv = isValid ? wantedEnv : 'dev';
28 | let config = require(path.join(__dirname, 'cfg/' + validEnv));
29 | return config;
30 | }
31 |
32 | module.exports = buildConfig(env);
33 |
--------------------------------------------------------------------------------
/web/src/reducers/search.js:
--------------------------------------------------------------------------------
1 |
2 | export function searchHasErrored(state = false, action) {
3 | switch (action.type) {
4 | case 'SEARCH_HAS_ERRORED':
5 | return action.hasErrored;
6 |
7 | default:
8 | return state;
9 | }
10 | }
11 |
12 | export function searchIsLoading(state = false, action) {
13 | switch (action.type) {
14 | case 'SEARCH_IS_LOADING':
15 | return action.isLoading;
16 |
17 | default:
18 | return state;
19 | }
20 | }
21 |
22 | export function search(state = {}, action) {
23 | switch (action.type) {
24 | case 'SEARCH_FETCH_DATA_SUCCESS':
25 | return action.search;
26 |
27 | default:
28 | return state;
29 | }
30 | }
31 |
32 |
33 | export function searchterm(state = '', action) {
34 | switch (action.type) {
35 | case 'SET_SEARCH_TERM':
36 | return action.searchterm;
37 |
38 | default:
39 | return state;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/web/test/helpers/shallowRenderHelper.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Function to get the shallow output for a given component
3 | * As we are using phantom.js, we also need to include the fn.proto.bind shim!
4 | *
5 | * @see http://simonsmith.io/unit-testing-react-components-without-a-dom/
6 | * @author somonsmith
7 | */
8 | import React from 'react';
9 | import TestUtils from 'react-addons-test-utils';
10 |
11 | /**
12 | * Get the shallow rendered component
13 | *
14 | * @param {Object} component The component to return the output for
15 | * @param {Object} props [optional] The components properties
16 | * @param {Mixed} ...children [optional] List of children
17 | * @return {Object} Shallow rendered output
18 | */
19 | export default function createComponent(component, props = {}, ...children) {
20 | const shallowRenderer = TestUtils.createRenderer();
21 | shallowRenderer.render(React.createElement(component, props, children.length > 1 ? children : children[0]));
22 | return shallowRenderer.getRenderOutput();
23 | }
24 |
--------------------------------------------------------------------------------
/web/src/actions/graph.js:
--------------------------------------------------------------------------------
1 | import { Api } from '../lib/api.js'
2 |
3 | export function graphHasErrored(bool) {
4 | return {
5 | type: 'GRAPH_HAS_ERRORED',
6 | hasErrored: bool
7 | };
8 | }
9 |
10 | export function graphIsLoading(bool) {
11 | return {
12 | type: 'GRAPH_IS_LOADING',
13 | isLoading: bool
14 | };
15 | }
16 |
17 | export function graphFetchDataSuccess(graph) {
18 | return {
19 | type: 'GRAPH_FETCH_DATA_SUCCESS',
20 | graph
21 | };
22 | }
23 |
24 |
25 | export function graphFetchData(url) {
26 | return (dispatch) => {
27 | dispatch(graphIsLoading(true));
28 |
29 | Api.get(url)
30 | .then((response) => {
31 | dispatch(graphIsLoading(false));
32 | return response;
33 | })
34 | .then((response) => response.json())
35 | .then((graph) => dispatch(graphFetchDataSuccess(graph)))
36 | .catch(() => dispatch(graphHasErrored(true)));
37 | };
38 | }
--------------------------------------------------------------------------------
/web/src/actions/users.js:
--------------------------------------------------------------------------------
1 | import { Api } from '../lib/api.js'
2 |
3 | export function usersHasErrored(bool) {
4 | return {
5 | type: 'USERS_HAS_ERRORED',
6 | hasErrored: bool
7 | };
8 | }
9 |
10 | export function usersIsLoading(bool) {
11 | return {
12 | type: 'USERS_IS_LOADING',
13 | isLoading: bool
14 | };
15 | }
16 |
17 | export function usersFetchDataSuccess(users) {
18 | return {
19 | type: 'USERS_FETCH_DATA_SUCCESS',
20 | users
21 | };
22 | }
23 |
24 |
25 | export function usersFetchData(url) {
26 | return (dispatch) => {
27 |
28 | dispatch(usersIsLoading(true));
29 |
30 | Api.get(url)
31 | .then((response) => {
32 | dispatch(usersIsLoading(false));
33 |
34 | return response;
35 | })
36 | .then((response) => response.json())
37 | .then((users) => dispatch(usersFetchDataSuccess(users)))
38 | .catch(() => dispatch(usersHasErrored(true)));
39 | };
40 | }
--------------------------------------------------------------------------------
/web/src/actions/metrics.js:
--------------------------------------------------------------------------------
1 | import { Api } from '../lib/api.js'
2 |
3 | export function metricsHasErrored(bool) {
4 | return {
5 | type: 'METRICS_HAS_ERRORED',
6 | hasErrored: bool
7 | };
8 | }
9 |
10 | export function metricsIsLoading(bool) {
11 | return {
12 | type: 'METRICS_IS_LOADING',
13 | isLoading: bool
14 | };
15 | }
16 |
17 | export function metricsFetchDataSuccess(metrics) {
18 | return {
19 | type: 'METRICS_FETCH_DATA_SUCCESS',
20 | metrics
21 | };
22 | }
23 |
24 |
25 | export function metricsFetchData(url) {
26 | return (dispatch) => {
27 | dispatch(metricsIsLoading(true));
28 |
29 | Api.get(url)
30 | .then((response) => {
31 | dispatch(metricsIsLoading(false));
32 | return response;
33 | })
34 | .then((response) => response.json())
35 | .then((metrics) => dispatch(metricsFetchDataSuccess(metrics)))
36 | .catch(() => dispatch(metricsHasErrored(true)));
37 | };
38 | }
--------------------------------------------------------------------------------
/web/src/components/SidebarComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Menu, Label, Input } from 'semantic-ui-react'
5 |
6 |
7 | require('styles//SideBar.css');
8 |
9 | class SidebarComponent extends React.Component {
10 | state = { activeItem: 'home' }
11 |
12 | handleItemClick = (e, { name }) => this.setState({ activeItem: name })
13 |
14 | render() {
15 | const { activeItem } = this.state
16 | return (
17 |
18 |
24 |
25 | );
26 | }
27 | }
28 |
29 | SidebarComponent.displayName = 'SidebarComponent';
30 |
31 | // Uncomment properties you need
32 | // SideBarComponent.propTypes = {};
33 | // SideBarComponent.defaultProps = {};
34 |
35 | export default SidebarComponent;
36 |
--------------------------------------------------------------------------------
/web/src/actions/plugins.js:
--------------------------------------------------------------------------------
1 | import { Api } from '../lib/api.js'
2 |
3 | export function pluginsHasErrored(bool) {
4 | return {
5 | type: 'PLUGINS_HAS_ERRORED',
6 | hasErrored: bool
7 | };
8 | }
9 |
10 | export function pluginsIsLoading(bool) {
11 | return {
12 | type: 'PLUGINS_IS_LOADING',
13 | isLoading: bool
14 | };
15 | }
16 |
17 | export function pluginsFetchDataSuccess(plugins) {
18 | return {
19 | type: 'PLUGINS_FETCH_DATA_SUCCESS',
20 | plugins
21 | };
22 | }
23 |
24 |
25 | export function pluginsFetchData(url) {
26 | return (dispatch) => {
27 |
28 | dispatch(pluginsIsLoading(true));
29 |
30 | Api.get(url)
31 | .then((response) => {
32 | dispatch(pluginsIsLoading(false));
33 |
34 | return response;
35 | })
36 | .then((response) => response.json())
37 | .then((plugins) => dispatch(pluginsFetchDataSuccess(plugins)))
38 | .catch(() => dispatch(pluginsHasErrored(true)));
39 | };
40 | }
--------------------------------------------------------------------------------
/web/src/actions/sensors.js:
--------------------------------------------------------------------------------
1 | import { Api } from '../lib/api.js'
2 |
3 | export function sensorsHasErrored(bool) {
4 | return {
5 | type: 'SENSORS_HAS_ERRORED',
6 | hasErrored: bool
7 | };
8 | }
9 |
10 | export function sensorsIsLoading(bool) {
11 | return {
12 | type: 'SENSORS_IS_LOADING',
13 | isLoading: bool
14 | };
15 | }
16 |
17 | export function sensorsFetchDataSuccess(sensors) {
18 | return {
19 | type: 'SENSORS_FETCH_DATA_SUCCESS',
20 | sensors
21 | };
22 | }
23 |
24 |
25 | export function sensorsFetchData(url) {
26 | return (dispatch) => {
27 |
28 | dispatch(sensorsIsLoading(true));
29 |
30 | Api.get(url)
31 | .then((response) => {
32 | dispatch(sensorsIsLoading(false));
33 |
34 | return response;
35 | })
36 | .then((response) => response.json())
37 | .then((sensors) => dispatch(sensorsFetchDataSuccess(sensors)))
38 | .catch(() => dispatch(sensorsHasErrored(true)));
39 | };
40 | }
--------------------------------------------------------------------------------
/shared/mltools.py:
--------------------------------------------------------------------------------
1 | """ ML TOOLS """
2 |
3 | import os
4 | import sys
5 | import nltk
6 | from textblob.classifiers import NaiveBayesClassifier
7 |
8 | pathname = os.path.dirname(sys.argv[0])
9 | fullpath = os.path.abspath(pathname)
10 | nltk.data.path.append(fullpath + "/data/nltk_data")
11 |
12 | train = [
13 | ('.254', 'gateway'),
14 | ('.1', 'gateway'),
15 | ('.2', 'host'),
16 | ('.3', 'host'),
17 | ('.78', 'host'),
18 | ]
19 |
20 |
21 | # WIP: CHECK FORWARDING STATE
22 | cl = NaiveBayesClassifier(train)
23 |
24 |
25 | def is_gateway(host):
26 | result = False
27 |
28 | tests = 1
29 | proba = 0
30 |
31 | splitted_ip = host['ip_addr'].split('.')[-1]
32 | # print(splitted_ip)
33 | prob_dist = cl.prob_classify(splitted_ip)
34 | # print(prob_dist.max())
35 | # print(round(prob_dist.prob("gateway"), 2))
36 | if cl.classify(splitted_ip) == 'gateway':
37 | result = True
38 |
39 | return result
40 |
41 | if __name__ == "__main__":
42 | hostest = {'ip_addr': "192.168.56.3"}
43 | print(is_gateway(hostest))
44 |
--------------------------------------------------------------------------------
/web/cfg/dev.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let path = require('path');
4 | let webpack = require('webpack');
5 | let baseConfig = require('./base');
6 | let defaultSettings = require('./defaults');
7 |
8 | // Add needed plugins here
9 | let BowerWebpackPlugin = require('bower-webpack-plugin');
10 |
11 | let config = Object.assign({}, baseConfig, {
12 | entry: [
13 | 'webpack-dev-server/client?http://127.0.0.1:' + defaultSettings.port,
14 | 'webpack/hot/only-dev-server',
15 | './src/index'
16 | ],
17 | cache: true,
18 | devtool: 'eval-source-map',
19 | plugins: [
20 | new webpack.HotModuleReplacementPlugin(),
21 | new webpack.NoErrorsPlugin(),
22 | new BowerWebpackPlugin({
23 | searchResolveModulesDirectories: false
24 | })
25 | ],
26 | module: defaultSettings.getDefaultModules()
27 | });
28 |
29 | // Add needed loaders to the defaults here
30 | config.module.loaders.push({
31 | test: /\.(js|jsx)$/,
32 | loader: 'react-hot!babel-loader',
33 | include: [].concat(
34 | config.additionalPaths,
35 | [ path.join(__dirname, '/../src') ]
36 | )
37 | });
38 |
39 | module.exports = config;
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 gbnk0
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/web/src/components/hostdetail/ServicesComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Label, List, Icon } from 'semantic-ui-react'
5 |
6 | require('styles/hostdetail/Services.css');
7 |
8 | class ServicesComponent extends React.Component {
9 | render() {
10 | return (
11 |
12 |
13 | {this.props.host.host.services.map((proto) => (
14 |
15 |
16 |
17 |
18 |
19 |
20 | {proto.ports.map(port =>
21 |
22 | )}
23 |
24 |
25 |
26 |
27 | ))}
28 |
29 |
30 | );
31 | }
32 | }
33 |
34 | ServicesComponent.displayName = 'HostdetailServicesComponent';
35 |
36 | // Uncomment properties you need
37 | // ServicesComponent.propTypes = {};
38 | // ServicesComponent.defaultProps = {};
39 |
40 | export default ServicesComponent;
41 |
--------------------------------------------------------------------------------
/web/src/actions/search.js:
--------------------------------------------------------------------------------
1 | import { Api } from '../lib/api.js'
2 |
3 | export function searchHasErrored(bool) {
4 | return {
5 | type: 'SEARCH_HAS_ERRORED',
6 | hasErrored: bool
7 | };
8 | }
9 |
10 | export function searchIsLoading(bool) {
11 | return {
12 | type: 'SEARCH_IS_LOADING',
13 | isLoading: bool
14 | };
15 | }
16 |
17 | export function searchFetchDataSuccess(search) {
18 | return {
19 | type: 'SEARCH_FETCH_DATA_SUCCESS',
20 | search
21 | };
22 | }
23 |
24 |
25 | export function setSearchTerm(searchterm) {
26 | return {
27 | type: 'SET_SEARCH_TERM',
28 | searchterm
29 | };
30 | }
31 |
32 | export function searchFetchData(url) {
33 | return (dispatch) => {
34 | dispatch(searchIsLoading(true));
35 |
36 | Api.get(url)
37 | .then((response) => {
38 | dispatch(searchIsLoading(false));
39 | return response;
40 | })
41 | .then((response) => response.json())
42 | .then((search) => dispatch(searchFetchDataSuccess(search)))
43 | .catch(() => dispatch(searchHasErrored(true)));
44 | };
45 | }
46 |
47 |
48 |
--------------------------------------------------------------------------------
/shared/dnstools.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import subprocess
3 |
4 | socket.setdefaulttimeout(2)
5 |
6 |
7 | def mdns_reverse(ip_addr):
8 | try:
9 | cmd = "/usr/bin/dig +time=2 -x {} @224.0.0.251 -p 5353".format(ip_addr)
10 | response = subprocess.run(cmd, stdout=subprocess.PIPE, shell=True)
11 | response = str(response.stdout)
12 | response = response.split('PTR')[2].split(';')[0]
13 | response = response.strip('\\t').strip('\\n')
14 | response = response[:-1].lower()
15 | except:
16 | response = ""
17 | pass
18 |
19 | return response
20 |
21 |
22 | # DNS Resolver
23 | def reverse_lookup(ip_addr):
24 | result = ""
25 | try:
26 | result = socket.gethostbyaddr(ip_addr)[0]
27 |
28 | except:
29 | result = mdns_reverse(ip_addr)
30 | pass
31 |
32 | result = "".join(result)
33 |
34 | if len(result) == 0:
35 | result = "Unknown"
36 |
37 | return str(result)
38 |
39 |
40 | # Get domain name from fqdn
41 | def get_domain_name(fqdn):
42 | domain_name = ""
43 | try:
44 | domain_name = fqdn.split('.')[1:]
45 | domain_name = ".".join(domain_name)
46 | except:
47 | pass
48 |
49 | return domain_name
50 |
--------------------------------------------------------------------------------
/web/cfg/dist.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let path = require('path');
4 | let webpack = require('webpack');
5 |
6 | let baseConfig = require('./base');
7 | let defaultSettings = require('./defaults');
8 |
9 | // Add needed plugins here
10 | let BowerWebpackPlugin = require('bower-webpack-plugin');
11 |
12 | let config = Object.assign({}, baseConfig, {
13 | entry: path.join(__dirname, '../src/index'),
14 | cache: false,
15 | devtool: 'sourcemap',
16 | plugins: [
17 | new webpack.optimize.DedupePlugin(),
18 | new webpack.DefinePlugin({
19 | 'process.env.NODE_ENV': '"production"'
20 | }),
21 | new BowerWebpackPlugin({
22 | searchResolveModulesDirectories: false
23 | }),
24 | new webpack.optimize.UglifyJsPlugin(),
25 | new webpack.optimize.OccurenceOrderPlugin(),
26 | new webpack.optimize.AggressiveMergingPlugin(),
27 | new webpack.NoErrorsPlugin()
28 | ],
29 | module: defaultSettings.getDefaultModules()
30 | });
31 |
32 | // Add needed loaders to the defaults here
33 | config.module.loaders.push({
34 | test: /\.(js|jsx)$/,
35 | loader: 'babel',
36 | include: [].concat(
37 | config.additionalPaths,
38 | [ path.join(__dirname, '/../src') ]
39 | )
40 | });
41 |
42 | module.exports = config;
43 |
--------------------------------------------------------------------------------
/core/utils/utils.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | from ipaddress import IPv4Network
4 |
5 |
6 | def is_private(string):
7 | """ Check if a network address is private or public """
8 | result = False
9 | result = IPv4Network(string, strict=False).is_private
10 |
11 | return result
12 |
13 |
14 |
15 | """ PLUGINS """
16 |
17 | def get_plugin_info(plugin_path):
18 |
19 | infos = {
20 | "version": "Unknown"
21 | }
22 |
23 | try:
24 | with open(plugin_path + '/plugin.json', 'r') as info_file:
25 | infos = json.load(info_file)
26 | except:
27 | pass
28 |
29 | return infos
30 |
31 |
32 | def list_plugins(path):
33 | """ list all plugins """
34 | plugins = []
35 | available_path = path + '/available/'
36 | for root, dirs, files in os.walk(available_path):
37 | for filename in dirs:
38 | plugin_path = available_path + filename
39 | plugin_info = get_plugin_info(plugin_path)
40 |
41 | plugin = {
42 | "path": plugin_path,
43 | }
44 |
45 | for k,v in plugin_info.items():
46 | plugin[k] = v
47 |
48 | plugins.append(plugin)
49 |
50 | return plugins
51 |
52 |
53 |
--------------------------------------------------------------------------------
/web/server.js:
--------------------------------------------------------------------------------
1 | /*eslint no-console:0 */
2 | 'use strict';
3 | require('core-js/fn/object/assign');
4 | const webpack = require('webpack');
5 | const WebpackDevServer = require('webpack-dev-server');
6 | const config = require('./webpack.config');
7 | const open = require('open');
8 |
9 | /**
10 | * Flag indicating whether webpack compiled for the first time.
11 | * @type {boolean}
12 | */
13 | let isInitialCompilation = true;
14 |
15 | const compiler = webpack(config);
16 |
17 | new WebpackDevServer(compiler, config.devServer)
18 | .listen(config.port, 'localhost', (err) => {
19 | if (err) {
20 | console.log(err);
21 | }
22 | console.log('Listening at localhost:' + config.port);
23 | });
24 |
25 | compiler.plugin('done', () => {
26 | if (isInitialCompilation) {
27 | // Ensures that we log after webpack printed its stats (is there a better way?)
28 | setTimeout(() => {
29 | console.log('\n✓ The bundle is now ready for serving!\n');
30 | console.log(' Open in iframe mode:\t\x1b[33m%s\x1b[0m', 'http://localhost:' + config.port + '/webpack-dev-server/');
31 | console.log(' Open in inline mode:\t\x1b[33m%s\x1b[0m', 'http://localhost:' + config.port + '/\n');
32 | console.log(' \x1b[33mHMR is active\x1b[0m. The bundle will automatically rebuild and live-update on changes.')
33 | }, 350);
34 | }
35 | isInitialCompilation = false;
36 | });
37 |
--------------------------------------------------------------------------------
/web/src/components/settings/UserlistComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Table, Icon } from 'semantic-ui-react'
5 |
6 | require('styles/settings/Userlist.css');
7 |
8 | class UserlistComponent extends React.Component {
9 | render() {
10 | return (
11 |
12 |
13 |
14 |
15 | ID
16 | Username
17 |
18 |
19 |
20 |
21 | {this.props.users.results.map((user) => (
22 |
23 |
24 | {user.id}
25 |
26 |
27 | {user.email}
28 |
29 |
30 | ))}
31 |
32 |
33 |
34 | );
35 | }
36 | }
37 |
38 | UserlistComponent.displayName = 'SettingsUserlistComponent';
39 |
40 | // Uncomment properties you need
41 | // UserlistComponent.propTypes = {};
42 | // UserlistComponent.defaultProps = {};
43 |
44 | export default UserlistComponent;
45 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | jobs:
2 | include:
3 | - stage: Tests
4 | language: python
5 | python: 3.5
6 | install:
7 | - pip3.5 install python-coveralls
8 | - pip3.5 install coverage
9 | - pip3.5 install -r requirements.txt
10 | script:
11 | - coverage run -a shared/iftools.py
12 | - coverage run -a shared/api_wrapper.py
13 | - coverage run -a shared/dnstools.py
14 | - coverage report
15 | after_success:
16 | - coveralls
17 |
18 | - stage: build-image-api
19 | script:
20 | - cp -rfp requirements.txt core/.
21 | - docker build -t $DOCKER_USER/nmt-api -f docker/dockerfile-core .
22 |
23 | after_success:
24 | - docker login -u $DOCKER_USER -p $DOCKER_PASS
25 | - docker tag $DOCKER_USER/nmt-api $DOCKER_USER/nmt-api:$TRAVIS_BUILD_NUMBER;
26 | - docker push $DOCKER_USER:$TRAVIS_BUILD_NUMBER;
27 | - docker push $DOCKER_USER/nmt-api:latest
28 |
29 |
30 | - stage: build-image-ui
31 | script:
32 | - docker build -t $DOCKER_USER/nmt-ui -f docker/dockerfile-web .
33 |
34 | after_success:
35 | - docker login -u $DOCKER_USER -p $DOCKER_PASS
36 | - docker tag $DOCKER_USER/nmt-ui $DOCKER_USER/nmt-ui:$TRAVIS_BUILD_NUMBER;
37 | - docker push $DOCKER_USER:$TRAVIS_BUILD_NUMBER;
38 | - docker push $DOCKER_USER/nmt-ui:latest
39 |
40 |
--------------------------------------------------------------------------------
/web/src/index.js:
--------------------------------------------------------------------------------
1 | import 'core-js/fn/object/assign';
2 | import React from 'react';
3 | import ReactDOM from 'react-dom';
4 | import 'semantic-ui-css/semantic.min.css';
5 |
6 | import MainContainer from './containers/MainContainer';
7 |
8 | import { Provider } from 'react-redux';
9 | import { configureStore, history } from './stores/configureStore';
10 | import { ConnectedRouter} from 'react-router-redux'
11 |
12 |
13 |
14 | export const initialState = {
15 | networks: {
16 | results: [],
17 | checked:[]
18 | },
19 | hosts: {
20 | results: [],
21 | network: {}
22 | },
23 | host: {
24 | host: {
25 | network: {},
26 | services: []
27 | }
28 | },
29 | metrics: {},
30 | users: {
31 | results: []
32 | },
33 | graph: {
34 | nodes: [],
35 | edges: []
36 | },
37 | sensors: {
38 | results: []
39 | },
40 | plugins: {
41 | results: []
42 | },
43 | search: {
44 | results: []
45 | },
46 | searchterm: '',
47 | sessionReducer: !!localStorage.jwt,
48 | user: {
49 | me: {}
50 | }
51 |
52 | };
53 | const store = configureStore(initialState, history);
54 |
55 |
56 | ReactDOM.render((
57 |
58 |
59 |
60 |
61 |
62 | ), document.getElementById('app'));
--------------------------------------------------------------------------------
/web/src/reducers/hosts.js:
--------------------------------------------------------------------------------
1 | export function hostsHasErrored(state = false, action) {
2 | switch (action.type) {
3 | case 'HOSTS_HAS_ERRORED':
4 | return action.hasErrored;
5 |
6 | default:
7 | return state;
8 | }
9 | }
10 |
11 | export function hostsIsLoading(state = false, action) {
12 | switch (action.type) {
13 | case 'HOSTS_IS_LOADING':
14 | return action.isLoading;
15 |
16 | default:
17 | return state;
18 | }
19 | }
20 |
21 | export function hosts(state = {}, action) {
22 | switch (action.type) {
23 | case 'HOSTS_FETCH_DATA_SUCCESS':
24 | return action.hosts;
25 |
26 | default:
27 | return state;
28 | }
29 | }
30 |
31 | export function hostHasErrored(state = false, action) {
32 | switch (action.type) {
33 | case 'HOST_HAS_ERRORED':
34 | return action.hasErrored;
35 |
36 | default:
37 | return state;
38 | }
39 | }
40 |
41 | export function hostIsLoading(state = false, action) {
42 | switch (action.type) {
43 | case 'HOST_IS_LOADING':
44 | return action.isLoading;
45 |
46 | default:
47 | return state;
48 | }
49 | }
50 |
51 | export function host(state = {}, action) {
52 | switch (action.type) {
53 | case 'HOST_FETCH_DATA_SUCCESS':
54 | return action.host;
55 |
56 | default:
57 | return state;
58 | }
59 | }
--------------------------------------------------------------------------------
/web/cfg/base.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | let path = require('path');
3 | let defaultSettings = require('./defaults');
4 |
5 | // Additional npm or bower modules to include in builds
6 | // Add all foreign plugins you may need into this array
7 | // @example:
8 | // let npmBase = path.join(__dirname, '../node_modules');
9 | // let additionalPaths = [ path.join(npmBase, 'react-bootstrap') ];
10 | let additionalPaths = [];
11 |
12 | module.exports = {
13 | additionalPaths: additionalPaths,
14 | port: defaultSettings.port,
15 | debug: true,
16 | devtool: 'eval',
17 | output: {
18 | path: path.join(__dirname, '/../dist/assets'),
19 | filename: 'app.js',
20 | publicPath: defaultSettings.publicPath
21 | },
22 | devServer: {
23 | contentBase: './src/',
24 | historyApiFallback: true,
25 | hot: true,
26 | port: defaultSettings.port,
27 | publicPath: defaultSettings.publicPath,
28 | noInfo: false
29 | },
30 | resolve: {
31 | extensions: ['', '.js', '.jsx'],
32 | alias: {
33 | actions: `${defaultSettings.srcPath}/actions/`,
34 | components: `${defaultSettings.srcPath}/components/`,
35 | sources: `${defaultSettings.srcPath}/sources/`,
36 | stores: `${defaultSettings.srcPath}/stores/`,
37 | styles: `${defaultSettings.srcPath}/styles/`,
38 | config: `${defaultSettings.srcPath}/config/` + process.env.REACT_WEBPACK_ENV,
39 | 'react/lib/ReactMount': 'react-dom/lib/ReactMount'
40 | }
41 | },
42 | module: {}
43 | };
44 |
--------------------------------------------------------------------------------
/web/src/components/SearchComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 |
5 | import { Icon, Table } from 'semantic-ui-react';
6 |
7 |
8 |
9 | require('styles//Search.css');
10 |
11 |
12 | class SearchComponent extends React.Component {
13 | constructor(props) {
14 | super(props);
15 | }
16 |
17 |
18 | render() {
19 | let content;
20 |
21 | if (this.props.hasErrored) {
22 | content = API Unavailable
;
23 | return content;
24 | }
25 |
26 | return (
27 |
45 | );
46 | }
47 | }
48 |
49 | SearchComponent.propTypes = {
50 |
51 | };
52 |
53 |
54 | SearchComponent.displayName = 'SearchComponent';
55 |
56 | export default SearchComponent;
57 |
--------------------------------------------------------------------------------
/web/src/components/GraphComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import Graph from 'react-graph-vis';
5 |
6 | require('styles//Graph.css');
7 |
8 |
9 | const options = {
10 | nodes: {
11 | shape: 'dot',
12 | size: 10,
13 | font: {
14 | size: 10
15 | },
16 | shapeProperties: {
17 | interpolation: false
18 | },
19 | shadow:true
20 | },
21 | edges: {
22 | width: 1,
23 | shadow:true
24 | },
25 | physics:{
26 | stabilization: false
27 | },
28 | autoResize: true
29 | };
30 |
31 | const events = {
32 | // select: function(event) {
33 | // // var { nodes, edges } = event;
34 | // // console.log('Selected nodes:');
35 | // // console.log(nodes);
36 | // // console.log('Selected edges:');
37 | // // console.log(edges);
38 | // }
39 |
40 | };
41 |
42 | class GraphComponent extends React.Component {
43 |
44 | componentDidMount(){
45 | this.network.fit();
46 |
47 | }
48 |
49 | setNetworkInstance = nw => {
50 | this.network = nw;
51 | }
52 |
53 | render() {
54 | return (
55 |
56 |
63 |
64 | );
65 | }
66 | }
67 |
68 | GraphComponent.displayName = 'GraphComponent';
69 |
70 |
71 | export default GraphComponent;
72 |
--------------------------------------------------------------------------------
/web/src/components/HomeComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import MetricsComponent from './home/MetricsComponent'
5 | import GraphComponent from './GraphComponent';
6 | import { Header, Segment } from 'semantic-ui-react';
7 | import { Grid } from 'semantic-ui-react'
8 |
9 | require('styles//Home.css');
10 |
11 | class HomeComponent extends React.Component {
12 |
13 | render() {
14 | let content;
15 |
16 | if (this.props.metricshasErrored) {
17 | content = API Unavailable
;
18 | return content;
19 | }
20 |
21 |
22 | return (
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | );
46 | }
47 | }
48 |
49 |
50 |
51 | HomeComponent.propTypes = {
52 |
53 |
54 | };
55 |
56 |
57 | HomeComponent.displayName = 'HomeComponent';
58 |
59 | export default HomeComponent;
60 |
--------------------------------------------------------------------------------
/web/src/components/home/MetricsComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Statistic } from 'semantic-ui-react'
5 |
6 | require('styles/home/Metrics.css');
7 |
8 | class MetricsComponent extends React.Component {
9 | render() {
10 | return (
11 |
12 |
13 |
14 | {this.props.metrics.network_count}
15 | Networks
16 |
17 |
18 | {this.props.metrics.host_count}
19 | Hosts
20 |
21 |
22 | {this.props.metrics.sensor_count}
23 | Sensors
24 |
25 |
26 | {this.props.metrics.ports_count}
27 | Open ports
28 |
29 |
30 |
31 | );
32 | }
33 | }
34 |
35 | MetricsComponent.displayName = 'HomeMetricsComponent';
36 |
37 | // Uncomment properties you need
38 | // MetricsComponent.propTypes = {};
39 | // MetricsComponent.defaultProps = {};
40 |
41 | export default MetricsComponent;
42 |
--------------------------------------------------------------------------------
/web/src/routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Route, Redirect, Switch } from 'react-router';
3 |
4 | import HomeContainer from './containers/HomeContainer'
5 | import NetworkListContainer from './containers/NetworkListContainer';
6 | import NetworkDetailContainer from './containers/NetworkDetailContainer'
7 | import HostDetailContainer from './containers/HostDetailContainer'
8 | import SearchContainer from './containers/SearchContainer'
9 | import LoginPageComponent from './components/LoginPageComponent.js'
10 | import SettingsContainer from './containers/SettingsContainer'
11 |
12 |
13 |
14 |
15 |
16 | class Routes extends React.Component {
17 | constructor(props) {
18 | super(props);
19 | }
20 |
21 |
22 |
23 | render() {
24 | return (
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | );
37 | }
38 |
39 |
40 | }
41 |
42 |
43 | export default Routes
--------------------------------------------------------------------------------
/web/src/reducers/sessions.js:
--------------------------------------------------------------------------------
1 |
2 | export function loginFailed(state = false, action) {
3 | switch (action.type) {
4 | case 'LOG_IN_FAILED':
5 | return action.isFailed;
6 |
7 | default:
8 | return state;
9 | }
10 | }
11 |
12 | export function loginLoading(state = false, action) {
13 | switch (action.type) {
14 | case 'LOG_IN_LOADING':
15 | return action.isLoading;
16 |
17 | default:
18 | return state;
19 | }
20 | }
21 |
22 |
23 | export function sessionReducer(state = '', action) {
24 | switch(action.type) {
25 |
26 | case 'LOG_IN_SUCCESS':
27 | return !!localStorage.jwt
28 |
29 | case 'LOG_OUT':
30 | return !!localStorage.jwt
31 |
32 | default:
33 | return state;
34 | }
35 | }
36 |
37 |
38 |
39 | export function userHasErrored(state = false, action) {
40 | switch (action.type) {
41 | case 'USER_HAS_ERRORED':
42 | return action.hasErrored;
43 |
44 | default:
45 | return state;
46 | }
47 | }
48 |
49 | export function userIsLoading(state = false, action) {
50 | switch (action.type) {
51 | case 'USER_IS_LOADING':
52 | return action.isLoading;
53 |
54 | default:
55 | return state;
56 | }
57 | }
58 |
59 | export function user(state = {}, action) {
60 | switch (action.type) {
61 | case 'USER_FETCH_DATA_SUCCESS':
62 | return action.user;
63 |
64 | default:
65 | return state;
66 | }
67 | }
68 |
69 |
--------------------------------------------------------------------------------
/web/cfg/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let path = require('path');
4 | let srcPath = path.join(__dirname, '/../src/');
5 |
6 | let baseConfig = require('./base');
7 |
8 | // Add needed plugins here
9 | let BowerWebpackPlugin = require('bower-webpack-plugin');
10 |
11 | module.exports = {
12 | devtool: 'eval',
13 | module: {
14 | preLoaders: [
15 | {
16 | test: /\.(js|jsx)$/,
17 | loader: 'isparta-instrumenter-loader',
18 | include: [
19 | path.join(__dirname, '/../src')
20 | ]
21 | }
22 | ],
23 | loaders: [
24 | {
25 | test: /\.(png|jpg|gif|woff|woff2|css|sass|scss|less|styl)$/,
26 | loader: 'null-loader'
27 | },
28 | {
29 | test: /\.(js|jsx)$/,
30 | loader: 'babel-loader',
31 | include: [].concat(
32 | baseConfig.additionalPaths,
33 | [
34 | path.join(__dirname, '/../src'),
35 | path.join(__dirname, '/../test')
36 | ]
37 | )
38 | }
39 | ]
40 | },
41 | resolve: {
42 | extensions: [ '', '.js', '.jsx' ],
43 | alias: {
44 | actions: srcPath + 'actions/',
45 | helpers: path.join(__dirname, '/../test/helpers'),
46 | components: srcPath + 'components/',
47 | sources: srcPath + 'sources/',
48 | stores: srcPath + 'stores/',
49 | styles: srcPath + 'styles/',
50 | config: srcPath + 'config/' + process.env.REACT_WEBPACK_ENV
51 | }
52 | },
53 | plugins: [
54 | new BowerWebpackPlugin({
55 | searchResolveModulesDirectories: false
56 | })
57 | ]
58 | };
59 |
--------------------------------------------------------------------------------
/web/src/components/BreadcrumbComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Breadcrumb } from 'semantic-ui-react'
5 | import PropTypes from 'prop-types';
6 |
7 | require('styles//Breadcrumb.css');
8 |
9 | class BreadcrumbComponent extends React.Component {
10 | static contextTypes = {
11 | router: PropTypes.object
12 | }
13 |
14 | constructor(props, context) {
15 | super(props, context);
16 | }
17 |
18 | navigate(route) {
19 | this.context.router.history.push(route);
20 | }
21 |
22 | handleClick = (e, { name }) => this.navigate(name)
23 |
24 | render() {
25 |
26 | return (
27 |
28 |
29 | Home
30 | {this.props.links ?
31 | (this.props.links.map((link) => (
32 | [
33 | ,
34 |
39 | {link.name}
40 |
41 | ]
42 |
43 | ))) : null
44 | }
45 |
46 |
47 | );
48 | }
49 | }
50 |
51 | BreadcrumbComponent.displayName = 'BreadcrumbComponent';
52 |
53 | // Uncomment properties you need
54 | // BreadcrumbComponent.propTypes = {};
55 | // BreadcrumbComponent.defaultProps = {};
56 |
57 | export default BreadcrumbComponent;
58 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | code/*.db
2 | assets.db
3 | # Byte-compiled / optimized / DLL files
4 | __pycache__/
5 | *.py[cod]
6 | *$py.class
7 |
8 | # C extensions
9 | *.so
10 |
11 | # Distribution / packaging
12 | .Python
13 | build/
14 | develop-eggs/
15 | dist/
16 | downloads/
17 | eggs/
18 | .eggs/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .coverage
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 |
49 | # Translations
50 | *.mo
51 | *.pot
52 |
53 | # Django stuff:
54 | *.log
55 | .static_storage/
56 | .media/
57 | local_settings.py
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
--------------------------------------------------------------------------------
/sensor/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 |
49 | # Translations
50 | *.mo
51 | *.pot
52 |
53 | # Django stuff:
54 | *.log
55 | .static_storage/
56 | .media/
57 | local_settings.py
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
--------------------------------------------------------------------------------
/shared/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 |
49 | # Translations
50 | *.mo
51 | *.pot
52 |
53 | # Django stuff:
54 | *.log
55 | .static_storage/
56 | .media/
57 | local_settings.py
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
--------------------------------------------------------------------------------
/web/src/reducers/networks.js:
--------------------------------------------------------------------------------
1 | export function networksHasErrored(state = false, action) {
2 | switch (action.type) {
3 | case 'NETWORKS_HAS_ERRORED':
4 | return action.hasErrored;
5 |
6 | default:
7 | return state;
8 | }
9 | }
10 |
11 | export function networksIsLoading(state = false, action) {
12 | switch (action.type) {
13 | case 'NETWORKS_IS_LOADING':
14 | return action.isLoading;
15 |
16 | default:
17 | return state;
18 | }
19 | }
20 |
21 | export function networks(state = {}, action) {
22 | switch (action.type) {
23 | case 'NETWORKS_FETCH_DATA_SUCCESS':
24 | return action.networks;
25 |
26 | default:
27 | return state;
28 | }
29 | }
30 |
31 |
32 |
33 |
34 | // POST DATA //
35 |
36 |
37 | export function networksPostHasErrored(state = false, action) {
38 | switch (action.type) {
39 | case 'NETWORKS_POST_HAS_ERRORED':
40 | return action.hasErrored;
41 |
42 | default:
43 | return state;
44 | }
45 | }
46 |
47 | export function networksPostIsLoading(state = false, action) {
48 | switch (action.type) {
49 | case 'NETWORKS_POST_IS_LOADING':
50 | return action.isLoading;
51 |
52 | default:
53 | return state;
54 | }
55 | }
56 |
57 | export function networksPostDataSuccess(state = false, action) {
58 | switch (action.type) {
59 | case 'NETWORKS_POST_DATA_SUCCESS':
60 | return action.isSuccess;
61 |
62 | default:
63 | return state;
64 | }
65 | }
66 |
67 |
68 | // CHECK NETWORKS CHECKBOX //
69 |
70 | export function addNetworkToList(state = [], action) {
71 | switch (action.type) {
72 | case 'ADD_NETWORK_TO_LIST':
73 | return action.networkId;
74 |
75 | default:
76 | return state;
77 | }
78 | }
--------------------------------------------------------------------------------
/web/cfg/defaults.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Function that returns default values.
3 | * Used because Object.assign does a shallow instead of a deep copy.
4 | * Using [].push will add to the base array, so a require will alter
5 | * the base array output.
6 | */
7 | 'use strict';
8 |
9 | const path = require('path');
10 | const srcPath = path.join(__dirname, '/../src');
11 | const dfltPort = 8000;
12 |
13 | /**
14 | * Get the default modules object for webpack
15 | * @return {Object}
16 | */
17 | function getDefaultModules() {
18 | return {
19 | preLoaders: [
20 | {
21 | test: /\.(js|jsx)$/,
22 | include: srcPath,
23 | loader: 'eslint-loader'
24 | }
25 | ],
26 | loaders: [
27 | {
28 | test: /\.css$/,
29 | loader: 'style-loader!css-loader'
30 | },
31 | {
32 | test: /\.sass/,
33 | loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded&indentedSyntax'
34 | },
35 | {
36 | test: /\.scss/,
37 | loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded'
38 | },
39 | {
40 | test: /\.less/,
41 | loader: 'style-loader!css-loader!less-loader'
42 | },
43 | {
44 | test: /\.styl/,
45 | loader: 'style-loader!css-loader!stylus-loader'
46 | },
47 | {
48 | test: /\.(mp4|ogg|svg)$/,
49 | loader: 'file-loader'
50 | },
51 | {
52 | test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
53 | loader: 'url-loader?limit=10000&mimetype=application/fontwoff'
54 | },
55 | {
56 | test: /\.jpe?g$|\.gif$|\.png$|\.ttf$|\.eot$|\.svg$/,
57 | loader: 'file-loader?name=[name].[ext]?[hash]'
58 | },
59 |
60 | ]
61 | };
62 | }
63 |
64 | module.exports = {
65 | srcPath: srcPath,
66 | publicPath: '/assets/',
67 | port: dfltPort,
68 | getDefaultModules: getDefaultModules
69 | };
70 |
--------------------------------------------------------------------------------
/tools/insert_fake_networks.py:
--------------------------------------------------------------------------------
1 | from shared import api_wrapper as apiwr
2 | from random import randint
3 | from datetime import datetime
4 |
5 | api_url = 'http://localhost:8080'
6 | login = 'admin'
7 | password = 'password'
8 |
9 | api = apiwr.CoreApi(
10 | url=api_url,
11 | login=login,
12 | password=password
13 | )
14 |
15 | fake_nets = []
16 | for i in range(0,1):
17 | fake_num = randint(0,250)
18 | fake_net = '192.168.{}.0/24'.format(fake_num)
19 | if not fake_net in fake_nets:
20 | fake_nets.append(fake_net)
21 | api.update_network(name='TEST_' + str(fake_num), ip_addr=fake_net)
22 | print(fake_net)
23 |
24 |
25 |
26 | for net in api.get_networks():
27 | host_count = randint(0,250)
28 | hosts = []
29 | cur_host = {
30 | 'ip_addr': ".".join(net['ip_addr'].split('/')[0].split('.')[:-1]) +'.' + str(254),
31 | 'dns_names': 'host_test_{}'.format(i),
32 | 'network_id': net['id'],
33 | 'mac_addr': '00:50:F3:{}{}:{}{}'.format(randint(0,9), randint(0,9), randint(0,9), randint(0,9)),
34 | 'mac_vendor': 'VMWare',
35 | 'last_seen': datetime.now().isoformat(),
36 | 'services': []
37 | }
38 | hosts.append(cur_host)
39 | for i in range(1, host_count):
40 | try:
41 | cur_host = {
42 | 'ip_addr': ".".join(net['ip_addr'].split('/')[0].split('.')[:-1]) +'.' + str(i),
43 | 'dns_names': 'host_test_{}'.format(i),
44 | 'network_id': net['id'],
45 | 'mac_addr': '00:50:F3:{}{}:{}{}'.format(randint(0,9), randint(0,9), randint(0,9), randint(0,9)),
46 | 'mac_vendor': 'VMWare',
47 | 'last_seen': datetime.now().isoformat(),
48 | 'services': []
49 | }
50 | hosts.append(cur_host)
51 | api.add_host(**cur_host)
52 | print(cur_host)
53 | except:
54 | raise
55 |
--------------------------------------------------------------------------------
/web/src/containers/SearchContainer.js:
--------------------------------------------------------------------------------
1 | require('normalize.css/normalize.css');
2 |
3 |
4 | import React from 'react';
5 | import HeaderComponent from '../components/HeaderComponent'
6 | import BreadcrumbComponent from '../components/BreadcrumbComponent'
7 | import SearchComponent from '../components/SearchComponent'
8 |
9 | // Redux
10 | import PropTypes from 'prop-types';
11 | import { connect } from 'react-redux';
12 | import { searchFetchData } from '../actions/search';
13 |
14 |
15 |
16 | class SearchContainer extends React.Component {
17 |
18 | render() {
19 | const links = [
20 | {
21 | 'route': '/search/',
22 | 'name': 'Search',
23 | 'active': true
24 | }
25 | ];
26 | return (
27 |
28 |
29 |
34 |
35 |
36 |
37 |
38 | );
39 | }
40 | }
41 |
42 |
43 | SearchContainer.propTypes = {
44 | fetchData: PropTypes.func.isRequired,
45 | search: PropTypes.object.isRequired,
46 | hasErrored: PropTypes.bool.isRequired,
47 | isLoading: PropTypes.bool.isRequired,
48 | searchterm: PropTypes.string.isRequired
49 | };
50 |
51 | const mapStateToProps = (state) => {
52 | return {
53 | search: state.search,
54 | hasErrored: state.searchHasErrored,
55 | isLoading: state.searchIsLoading,
56 | searchterm: state.searchterm
57 | };
58 | };
59 |
60 | const mapDispatchToProps = (dispatch) => {
61 | return {
62 | fetchData: (url) => dispatch(searchFetchData(url))
63 | };
64 | };
65 |
66 | SearchContainer.displayName = 'SearchContainer';
67 |
68 | export default connect(mapStateToProps, mapDispatchToProps)(SearchContainer);
69 |
--------------------------------------------------------------------------------
/web/src/components/settings/PluginlistComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Table, Icon, Button } from 'semantic-ui-react'
5 |
6 | require('styles/settings/Pluginlist.css');
7 |
8 | class PluginlistComponent extends React.Component {
9 |
10 |
11 | render() {
12 | if (this.props.pluginsisLoading) {
13 |
14 | const content = Loading...
;
15 | return content;
16 | }
17 |
18 | return (
19 |
20 |
21 |
22 |
23 | Name
24 | Type
25 | Version
26 | Actions
27 |
28 |
29 |
30 |
31 | {this.props.plugins.results.map((plugin) => (
32 |
33 |
34 | {plugin.name}
35 |
36 |
37 | Scanner
38 |
39 |
40 | {plugin.version}
41 |
42 |
43 |
44 |
45 |
46 | ))}
47 |
48 |
49 |
50 | );
51 | }
52 | }
53 |
54 | PluginlistComponent.displayName = 'SettingsPluginlistComponent';
55 |
56 | // Uncomment properties you need
57 | // PluginlistComponent.propTypes = {};
58 | // PluginlistComponent.defaultProps = {};
59 |
60 | export default PluginlistComponent;
61 |
--------------------------------------------------------------------------------
/web/src/actions/hosts.js:
--------------------------------------------------------------------------------
1 | import { Api } from '../lib/api.js'
2 |
3 | export function hostsHasErrored(bool) {
4 | return {
5 | type: 'HOSTS_HAS_ERRORED',
6 | hasErrored: bool
7 | };
8 | }
9 |
10 | export function hostsIsLoading(bool) {
11 | return {
12 | type: 'HOSTS_IS_LOADING',
13 | isLoading: bool
14 | };
15 | }
16 |
17 | export function hostsFetchDataSuccess(hosts) {
18 | return {
19 | type: 'HOSTS_FETCH_DATA_SUCCESS',
20 | hosts
21 | };
22 | }
23 |
24 | export function hostHasErrored(bool) {
25 | return {
26 | type: 'HOST_HAS_ERRORED',
27 | hasErrored: bool
28 | };
29 | }
30 |
31 | export function hostIsLoading(bool) {
32 | return {
33 | type: 'HOST_IS_LOADING',
34 | isLoading: bool
35 | };
36 | }
37 |
38 | export function hostFetchDataSuccess(host) {
39 | return {
40 | type: 'HOST_FETCH_DATA_SUCCESS',
41 | host
42 | };
43 | }
44 |
45 |
46 | export function hostFetchData(url) {
47 | return (dispatch) => {
48 | dispatch(hostIsLoading(true));
49 |
50 | Api.get(url)
51 | .then((response) => {
52 | dispatch(hostIsLoading(false));
53 | return response;
54 | })
55 | .then((response) => response.json())
56 | .then((host) => dispatch(hostFetchDataSuccess(host)))
57 | .catch(() => dispatch(hostHasErrored(true)));
58 |
59 | };
60 | }
61 |
62 | export function hostsFetchData(url) {
63 | return (dispatch) => {
64 | dispatch(hostsIsLoading(true));
65 |
66 | Api.get(url)
67 | .then((response) => {
68 |
69 | dispatch(hostsIsLoading(false));
70 |
71 | return response;
72 | })
73 | .then((response) => response.json())
74 | .then((hosts) => dispatch(hostsFetchDataSuccess(hosts)))
75 | .catch(() => dispatch(hostsHasErrored(true)));
76 |
77 | };
78 | }
79 |
80 |
81 |
--------------------------------------------------------------------------------
/web/src/reducers/index.js:
--------------------------------------------------------------------------------
1 |
2 | import { combineReducers } from 'redux';
3 |
4 | import { networks, networksHasErrored, networksIsLoading } from './networks';
5 | import { networksPostDataSuccess, networksPostIsLoading, networksPostHasErrored } from './networks';
6 | import { addNetworkToList } from './networks'
7 | import { hosts, hostsHasErrored, hostsIsLoading, host, hostHasErrored, hostIsLoading } from './hosts';
8 | import { metrics, metricsHasErrored, metricsIsLoading } from './metrics';
9 | import { graph, graphHasErrored, graphIsLoading } from './graph';
10 | import { sensors, sensorsHasErrored, sensorsIsLoading } from './sensors';
11 | import { plugins, pluginsHasErrored, pluginsIsLoading } from './plugins';
12 | import { users, usersHasErrored, usersIsLoading } from './users';
13 | import { search, searchHasErrored, searchIsLoading, searchterm } from './search';
14 |
15 | import { sessionReducer, loginLoading, loginFailed, userHasErrored, userIsLoading, user } from './sessions';
16 |
17 | import { routerReducer } from 'react-router-redux'
18 | import { reducer as notifications } from 'react-notification-system-redux';
19 |
20 | export default combineReducers({
21 | networks,
22 | networksHasErrored,
23 | networksIsLoading,
24 | networksPostDataSuccess,
25 | networksPostIsLoading,
26 | networksPostHasErrored,
27 | addNetworkToList,
28 | hosts,
29 | hostsHasErrored,
30 | hostsIsLoading,
31 | host,
32 | hostHasErrored,
33 | hostIsLoading,
34 | metrics,
35 | metricsHasErrored,
36 | metricsIsLoading,
37 | graph,
38 | graphHasErrored,
39 | graphIsLoading,
40 | sensors,
41 | sensorsHasErrored,
42 | sensorsIsLoading,
43 | plugins,
44 | pluginsHasErrored,
45 | pluginsIsLoading,
46 | users,
47 | usersHasErrored,
48 | usersIsLoading,
49 | search,
50 | searchHasErrored,
51 | searchIsLoading,
52 | searchterm,
53 | sessionReducer,
54 | loginLoading,
55 | loginFailed,
56 | userHasErrored,
57 | userIsLoading,
58 | user,
59 | router: routerReducer,
60 | notifications
61 | });
--------------------------------------------------------------------------------
/web/src/containers/HomeContainer.js:
--------------------------------------------------------------------------------
1 | require('normalize.css/normalize.css');
2 |
3 |
4 | import React from 'react';
5 |
6 | // Redux
7 | import PropTypes from 'prop-types';
8 | import { connect } from 'react-redux';
9 |
10 | import { graphFetchData } from '../actions/graph';
11 | import { metricsFetchData } from '../actions/metrics';
12 |
13 | import HomeComponent from '../components/HomeComponent'
14 | import BreadcrumbComponent from '../components/BreadcrumbComponent'
15 | import HeaderComponent from '../components/HeaderComponent'
16 |
17 |
18 | class HomeContainer extends React.Component {
19 | constructor(props) {
20 | super(props);
21 | }
22 |
23 | componentDidMount() {
24 | if (localStorage.jwt) {
25 | this.props.metricsFetchData('/metrics/');
26 | this.props.graphFetchData('/graphs/');
27 | }
28 | }
29 |
30 |
31 | render() {
32 | const links = [];
33 | return (
34 |
35 |
36 |
37 |
42 |
43 |
44 |
45 |
46 |
47 | );
48 | }
49 | }
50 |
51 |
52 | HomeContainer.propTypes = {
53 | metricsFetchData: PropTypes.func.isRequired,
54 | metrics: PropTypes.object.isRequired,
55 | metricshasErrored: PropTypes.bool.isRequired,
56 | metricsisLoading: PropTypes.bool.isRequired,
57 | graphFetchData: PropTypes.func.isRequired,
58 | graph: PropTypes.object.isRequired,
59 | graphhasErrored: PropTypes.bool.isRequired,
60 | graphisLoading: PropTypes.bool.isRequired
61 | };
62 |
63 | const mapStateToProps = (state) => {
64 | return {
65 | metrics: state.metrics,
66 | metricshasErrored: state.metricsHasErrored,
67 | metricsisLoading: state.metricsIsLoading,
68 | graph: state.graph,
69 | graphhasErrored: state.graphHasErrored,
70 | graphisLoading: state.graphIsLoading
71 | };
72 | };
73 |
74 | const mapDispatchToProps = (dispatch) => {
75 | return {
76 | metricsFetchData: (url) => dispatch(metricsFetchData(url)),
77 | graphFetchData: (url) => dispatch(graphFetchData(url))
78 | };
79 | };
80 |
81 | HomeContainer.displayName = 'HomeContainer';
82 |
83 | export default connect(mapStateToProps, mapDispatchToProps)(HomeContainer);
84 |
--------------------------------------------------------------------------------
/shared/iftools.py:
--------------------------------------------------------------------------------
1 | from attrdict import AttrDict
2 | import netifaces as ni
3 | from ipaddress import ip_address
4 | import re
5 |
6 |
7 | def is_mac(mac):
8 | result = False
9 |
10 | result = bool(
11 | re.match('^' + '[\:\-]'.join(['([0-9a-f]{2})']*6) + '$', mac.lower()))
12 |
13 | return result
14 |
15 |
16 | def is_ip(ip):
17 | result = False
18 | try:
19 | result = ip_address(ip)
20 | if result:
21 | result = True
22 | except:
23 | pass
24 | return result
25 |
26 |
27 | def sensor_ifaces():
28 | interfaces = []
29 |
30 | sensor_interfaces = ni.interfaces()
31 |
32 | for iface in sensor_interfaces:
33 | if not 'lo' in iface:
34 | interface = {
35 | 'name': iface,
36 | }
37 |
38 | ifaddrs = ni.ifaddresses(iface).items()
39 |
40 | for info in ifaddrs:
41 |
42 | addr = info[1][0]['addr']
43 |
44 |
45 | if is_mac(addr):
46 | interface['mac_addr'] = addr
47 |
48 | if is_ip(addr):
49 | interface['ip_addr'] = addr
50 |
51 | mask_infos = info[1][0]
52 |
53 | if 'netmask' in mask_infos:
54 | netmask = mask_infos['netmask']
55 | if is_ip(netmask):
56 | interface['netmask'] = netmask
57 |
58 |
59 |
60 | if 'ip_addr' in interface.keys():
61 | interface = AttrDict(interface)
62 | if not interface in interfaces:
63 | interfaces.append(interface)
64 |
65 | return interfaces
66 |
67 |
68 | def which_iface(ip):
69 | result = None
70 | # Remove netmask
71 | if "/" in ip:
72 | ip = ip.split('/')[0]
73 |
74 | # Remove last digit
75 | ip = ip.split('.')[:-1]
76 | ip = ".".join(ip)
77 |
78 | for iface in sensor_ifaces():
79 | try:
80 | if hasattr(iface, 'ip_addr'):
81 | sensorip = iface.ip_addr
82 | if sensorip.startswith(ip):
83 | result = iface.name
84 |
85 | except ValueError:
86 | pass
87 |
88 | return result
89 |
90 | if __name__ == "__main__":
91 | print("INTERFACES", sensor_ifaces())
92 | print("IS IP: ", is_ip("192.168.56.22"))
93 | print("WHICH IFACE:", which_iface("192.168.56.22"))
94 | print("IS MAC:", is_mac("0a:00:27:00:00:00"))
95 |
--------------------------------------------------------------------------------
/web/src/components/settings/SensorListComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Card, Button, Table } from 'semantic-ui-react';
5 |
6 | require('styles/settings/SensorList.css');
7 |
8 | class SensorListComponent extends React.Component {
9 | render() {
10 | if (this.props.isLoading) {
11 | return null;
12 | }
13 | return (
14 |
15 |
16 | {this.props.sensors.results.map((sensor) => (
17 |
18 |
19 |
20 |
21 | {sensor.name}
22 |
23 |
24 | {sensor.dns_names}
25 |
26 |
27 | {sensor.version}
28 | {sensor.ip_addr}
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | iFace
37 | IP Address
38 |
39 |
40 |
41 |
42 | {sensor.interfaces.map((iface) => (
43 |
44 | {iface.name}
45 | {iface.ip_addr}
46 |
47 | ))}
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | ))}
60 |
61 |
62 | );
63 | }
64 | }
65 |
66 | SensorListComponent.displayName = 'SensorListComponent';
67 |
68 | // Uncomment properties you need
69 | // SensorListComponent.propTypes = {};
70 | // SensorListComponent.defaultProps = {};
71 |
72 | export default SensorListComponent;
73 |
--------------------------------------------------------------------------------
/web/src/containers/NetworkListContainer.js:
--------------------------------------------------------------------------------
1 | require('normalize.css/normalize.css');
2 |
3 |
4 | import React from 'react';
5 | import NetworkListComponent from '../components/NetworkListComponent'
6 | import HeaderComponent from '../components/HeaderComponent'
7 | import BreadcrumbComponent from '../components/BreadcrumbComponent'
8 | import SidebarComponent from '../components/SidebarComponent'
9 | import { Grid } from 'semantic-ui-react'
10 | // Redux
11 | import PropTypes from 'prop-types';
12 | import { connect } from 'react-redux';
13 | import { networksFetchData, networksDeleteData } from '../actions/networks';
14 |
15 | class NetworkListContainer extends React.Component {
16 |
17 | componentDidMount() {
18 | this.props.fetchData('/networks/');
19 | }
20 |
21 | render() {
22 | const links = [
23 | {
24 | 'route': '/networks/',
25 | 'name': 'Networks',
26 | 'active': true
27 | }
28 | ];
29 | return (
30 |
31 |
32 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | );
51 | }
52 | }
53 |
54 |
55 | NetworkListContainer.propTypes = {
56 | fetchData: PropTypes.func.isRequired,
57 | networks: PropTypes.object.isRequired,
58 | hasErrored: PropTypes.bool.isRequired,
59 | isLoading: PropTypes.bool.isRequired,
60 | networksDeleteData: PropTypes.func.isRequired
61 | };
62 |
63 | const mapStateToProps = (state) => {
64 | return {
65 | networks: state.networks,
66 | hasErrored: state.networksHasErrored,
67 | isLoading: state.networksIsLoading
68 | };
69 | };
70 |
71 | const mapDispatchToProps = (dispatch) => {
72 | return {
73 | fetchData: (url) => dispatch(networksFetchData(url)),
74 | networksDeleteData: (url, networks) => dispatch(networksDeleteData(url, networks))
75 | };
76 | };
77 |
78 | NetworkListContainer.displayName = 'NetworkListContainer';
79 |
80 | export default connect(mapStateToProps, mapDispatchToProps)(NetworkListContainer);
81 |
--------------------------------------------------------------------------------
/web/src/containers/HostDetailContainer.js:
--------------------------------------------------------------------------------
1 | require('normalize.css/normalize.css');
2 |
3 |
4 | import React from 'react';
5 | import HostDetailComponent from '../components/HostDetailComponent'
6 | import BreadcrumbComponent from '../components/BreadcrumbComponent'
7 | import HeaderComponent from '../components/HeaderComponent'
8 |
9 | // Redux
10 | import PropTypes from 'prop-types';
11 | import { connect } from 'react-redux';
12 | import { hostFetchData } from '../actions/hosts';
13 |
14 | class HostDetailContainer extends React.Component {
15 | constructor(props) {
16 | super(props);
17 | }
18 |
19 | componentWillMount() {
20 | this.props.fetchData('/hosts/' + this.props.match.params.id + '/');
21 | }
22 |
23 | getbreadcrumblinks(props) {
24 | var links = [
25 | {
26 | 'route': '/networks/',
27 | 'name': 'Networks',
28 | 'active': false
29 | },
30 | {
31 | 'route': '/networks/' + props.host.host.network.id,
32 | 'name': props.host.host.network.name,
33 | 'active': false
34 | },
35 | {
36 | 'route': '/hosts/' + props.host.host.id,
37 | 'name': props.host.host.ip_addr,
38 | 'active': true
39 | }
40 | ];
41 | return links;
42 | }
43 |
44 | render() {
45 |
46 | let content;
47 |
48 | if (this.props.hasErrored) {
49 | content = Sorry! There was an error
;
50 | return content;
51 | }
52 |
53 | return (
54 |
55 |
57 |
62 |
63 |
64 | );
65 |
66 | }
67 | }
68 |
69 |
70 | HostDetailContainer.propTypes = {
71 | fetchData: PropTypes.func.isRequired,
72 | host: PropTypes.object.isRequired,
73 | hasErrored: PropTypes.bool.isRequired,
74 | isLoading: PropTypes.bool.isRequired
75 |
76 | };
77 |
78 | const mapStateToProps = (state) => {
79 | return {
80 | host: state.host,
81 | hasErrored: state.hostHasErrored,
82 | isLoading: state.hostIsLoading
83 |
84 | };
85 | };
86 |
87 | const mapDispatchToProps = (dispatch) => {
88 | return {
89 | fetchData: (url) => dispatch(hostFetchData(url))
90 | };
91 | };
92 |
93 | HostDetailContainer.displayName = 'HostDetailContainer';
94 |
95 | export default connect(mapStateToProps, mapDispatchToProps)(HostDetailContainer);
96 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NMT
2 |
3 | A network mapper / scanner for improving the pentesters/admin/engineers knowledge of local networks (maybe public soon).
4 |
5 |
6 |
7 | 
8 | 
9 | 
10 | 
11 | 
12 |
13 |
14 |
15 |
16 | ## DOCUMENTATION
17 | [Jump to the documentation](https://github.com/gbnk0/nmt/wiki)
18 |
19 |
20 | ## QUICK START
21 |
22 | ```bash
23 | cd docker/
24 | docker-compose -f docker-compose-all.yml up --build
25 | ```
26 |
27 | Go to localhost:8000 in your browser and login with the default credentials
28 |
29 | You can now add the first network to scan and launch the sensor:
30 |
31 |
32 | ```bash
33 | sudo pip3 install -r requirements.txt
34 | cd sensor/
35 | python3 sensor.py
36 | ```
37 | The scan process will take a time, depending on your sensor.conf config file.
38 |
39 | ##### DEFAULT CREDENTIALS
40 | `admin / password`
41 |
42 |
43 | ## INSTALLATION (DEV)
44 | ```bash
45 | git clone git@gitlab.com:gbnk0/nmt.git
46 | ```
47 | ### DEVELOPEMENT ENV REQUIREMENTS
48 | - Minimum python version: 3.5
49 | - Install docker:
50 | ```bash
51 | curl https://get.docker.com/|sudo sh
52 | ```
53 |
54 | - Install docker-compose:
55 | ```bash
56 | pip3 install docker-compose
57 | ```
58 |
59 | - Install NodeJS 8 LTS:
60 | ```bash
61 | curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
62 | sudo apt-get install -y nodejs
63 | ```
64 |
65 | - Install requirements:
66 | ```bash
67 | pip3 install -r requirements.txt
68 | ```
69 |
70 | ### CORE API
71 | #### LAUNCH THE API
72 | - Move into the core folder:
73 |
74 | ```bash
75 | cd core/
76 | ```
77 |
78 | - The api needs a RethinkDB instance for storing hosts, networks data, we'll use docker-compose for bringing it up:
79 |
80 | ```bash
81 | docker-compose -f docker/docker-compose.yml up #( add -d for background )
82 |
83 | ```
84 |
85 | - Now you can install dependencies, and run the api with sanic-admin:
86 |
87 | ```bash
88 | pip3 install sanic-admin
89 | sanic-admin api.py
90 | ```
91 |
92 |
93 | #### LAUNCH THE SENSOR
94 | ```bash
95 | cd sensor/
96 | python3 sensor.py
97 | ```
98 |
99 |
100 | ### WEB UI
101 | #### LAUNCH THE WEB UI (DEV MODE)
102 | ( Soon with docker-compose UP)
103 |
104 | ```bash
105 | cd web/
106 | npm install # (wait...)
107 | npm start
108 | ```
109 |
110 |
111 | ## License
112 | [](https://app.fossa.io/projects/git%2Bgithub.com%2Fgbnk0%2Fnmt?ref=badge_large)
113 |
--------------------------------------------------------------------------------
/web/src/containers/NetworkDetailContainer.js:
--------------------------------------------------------------------------------
1 | require('normalize.css/normalize.css');
2 |
3 |
4 | import React from 'react';
5 | import { Grid } from 'semantic-ui-react'
6 | import NetworkDetailComponent from '../components/NetworkDetailComponent'
7 | import BreadcrumbComponent from '../components/BreadcrumbComponent'
8 | import HeaderComponent from '../components/HeaderComponent'
9 |
10 | // Redux
11 | import PropTypes from 'prop-types';
12 | import { connect } from 'react-redux';
13 | import { hostsFetchData } from '../actions/hosts';
14 | // import { graphFetchData } from '../actions/graph';
15 |
16 |
17 | class NetworkDetailsContainer extends React.Component {
18 | constructor(props) {
19 | super(props);
20 | }
21 |
22 | componentDidMount() {
23 | const networkId = this.props.match.params.id
24 | this.props.fetchData('/networks/' + networkId + '/');
25 | // this.props.graphFetchData('/graphs/' + networkId + '/');
26 | }
27 |
28 | render() {
29 | const links = [
30 | {
31 | 'route': '/networks/',
32 | 'name': 'Networks',
33 | 'active': false
34 | },
35 | {
36 | 'route': '/networks/' + this.props.hosts.network.id,
37 | 'name': this.props.hosts.network.name,
38 | 'active': true
39 | }
40 | ];
41 | return (
42 |
43 |
45 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | );
58 | }
59 | }
60 |
61 |
62 | NetworkDetailsContainer.propTypes = {
63 | fetchData: PropTypes.func.isRequired,
64 | hosts: PropTypes.object.isRequired,
65 | hasErrored: PropTypes.bool.isRequired,
66 | isLoading: PropTypes.bool.isRequired,
67 | // graphFetchData: PropTypes.func.isRequired,
68 | // graph: PropTypes.object.isRequired,
69 | // graphhasErrored: PropTypes.bool.isRequired,
70 | // graphisLoading: PropTypes.bool.isRequired
71 |
72 | };
73 |
74 | const mapStateToProps = (state) => {
75 | return {
76 | hosts: state.hosts,
77 | hasErrored: state.hostsHasErrored,
78 | isLoading: state.hostsIsLoading,
79 | // graph: state.graph,
80 | // graphhasErrored: state.graphHasErrored,
81 | // graphisLoading: state.graphIsLoading
82 |
83 | };
84 | };
85 |
86 | const mapDispatchToProps = (dispatch) => {
87 | return {
88 | fetchData: (url) => dispatch(hostsFetchData(url))
89 | // graphFetchData: (url) => dispatch(graphFetchData(url))
90 | };
91 | };
92 |
93 | NetworkDetailsContainer.displayName = 'NetworkDetailsContainer';
94 |
95 | export default connect(mapStateToProps, mapDispatchToProps)(NetworkDetailsContainer);
96 |
--------------------------------------------------------------------------------
/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "version": "0.0.1",
4 | "description": "YOUR DESCRIPTION - Generated by generator-react-webpack",
5 | "main": "",
6 | "scripts": {
7 | "clean": "rimraf dist/*",
8 | "copy": "copyfiles -f ./src/index.html ./src/favicon.ico ./dist",
9 | "dist": "npm run copy & webpack --env=dist",
10 | "lint": "eslint ./src",
11 | "posttest": "npm run lint",
12 | "release:major": "npm version major && npm publish && git push --follow-tags",
13 | "release:minor": "npm version minor && npm publish && git push --follow-tags",
14 | "release:patch": "npm version patch && npm publish && git push --follow-tags",
15 | "serve": "node server.js --env=dev",
16 | "serve:dist": "node server.js --env=dist",
17 | "start": "node server.js --env=dev",
18 | "test": "karma start",
19 | "test:watch": "karma start --autoWatch=true --singleRun=false"
20 | },
21 | "repository": "",
22 | "keywords": [],
23 | "author": "Your name here",
24 | "devDependencies": {
25 | "babel-core": "^6.0.0",
26 | "babel-eslint": "^6.0.0",
27 | "babel-loader": "^6.0.0",
28 | "babel-polyfill": "^6.3.14",
29 | "babel-preset-es2015": "^6.0.15",
30 | "babel-preset-react": "^6.0.15",
31 | "babel-preset-stage-0": "^6.5.0",
32 | "bower-webpack-plugin": "^0.1.9",
33 | "chai": "^3.2.0",
34 | "copyfiles": "^1.0.0",
35 | "css-loader": "^0.23.0",
36 | "eslint": "^3.0.0",
37 | "eslint-loader": "^1.0.0",
38 | "eslint-plugin-react": "^6.0.0",
39 | "file-loader": "^0.9.0",
40 | "glob": "^7.0.0",
41 | "isparta-instrumenter-loader": "^1.0.0",
42 | "karma": "^1.0.0",
43 | "karma-chai": "^0.1.0",
44 | "karma-coverage": "^1.0.0",
45 | "karma-mocha": "^1.0.0",
46 | "karma-mocha-reporter": "^2.0.0",
47 | "karma-phantomjs-launcher": "^1.0.0",
48 | "karma-sourcemap-loader": "^0.3.5",
49 | "karma-webpack": "^1.7.0",
50 | "minimist": "^1.2.0",
51 | "mocha": "^3.0.0",
52 | "null-loader": "^0.1.1",
53 | "open": "0.0.5",
54 | "react-addons-test-utils": "^15.0.0",
55 | "react-hot-loader": "^1.2.9",
56 | "rimraf": "^2.4.3",
57 | "style-loader": "^0.13.0",
58 | "url-loader": "^0.5.9",
59 | "webpack": "^1.12.0",
60 | "webpack-dev-server": "^1.12.0"
61 | },
62 | "dependencies": {
63 | "components": "^0.1.0",
64 | "config": "^1.29.0",
65 | "core-js": "^2.0.0",
66 | "helpers": "^0.0.6",
67 | "history": "^4.7.2",
68 | "ip-address": "^5.8.8",
69 | "moment": "^2.19.1",
70 | "normalize.css": "^4.0.0",
71 | "prop-types": "^15.6.0",
72 | "react": "^15.0.0",
73 | "react-dom": "^15.0.0",
74 | "react-graph-vis": "^1.0.2",
75 | "react-notification-system": "^0.2.16",
76 | "react-notification-system-redux": "^1.2.0",
77 | "react-redux": "^5.0.6",
78 | "react-router": "^4.2.0",
79 | "react-router-dom": "^4.2.2",
80 | "react-router-redux": "^5.0.0-alpha.8",
81 | "redux": "^3.7.2",
82 | "redux-thunk": "^2.2.0",
83 | "semantic-ui-css": "^2.2.12",
84 | "semantic-ui-react": "^0.75.1",
85 | "styles": "^0.2.1"
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/web/src/components/NetworkDetailComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Icon, Table } from 'semantic-ui-react';
5 |
6 | import { formatDate, renderOs } from '../lib/tools';
7 | import PropTypes from 'prop-types';
8 |
9 | require('styles//NetworkDetail.css');
10 |
11 | class NetworkDetailComponent extends React.Component {
12 | static contextTypes = {
13 | router: PropTypes.object
14 | }
15 |
16 | constructor(props, context) {
17 | super(props, context);
18 | }
19 | navigate(route) {
20 | this.props.history.push('/hosts/' + route);
21 | }
22 |
23 | handleClick = (e, { key }) => this.navigate(key)
24 |
25 | render() {
26 | let content;
27 |
28 | if (this.props.hasErrored) {
29 | content = API Unavailable
;
30 | return content;
31 | }
32 | return (
33 |
75 | );
76 |
77 | }
78 | }
79 |
80 |
81 |
82 | NetworkDetailComponent.displayName = 'NetworkDetailComponent';
83 |
84 | // Uncomment properties you need
85 | // NetworkDetailComponent.propTypes = {};
86 | // NetworkDetailComponent.defaultProps = {};
87 |
88 | export default NetworkDetailComponent;
--------------------------------------------------------------------------------
/shared/api_wrapper.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import json
3 | from attrdict import AttrDict
4 |
5 | conn_headers = {
6 | 'Content-type': 'application/json'
7 | }
8 |
9 | class CoreApi:
10 |
11 | def __init__(self, url, **kwargs):
12 | self.url = url
13 | self.login = kwargs.get('login')
14 | self.password = kwargs.get('password')
15 | self.headers = conn_headers
16 |
17 | if self.login or self.password:
18 | self.headers['Authorization'] = self.get_auth_token()
19 |
20 | self.check = requests.get(url + "/check")
21 |
22 | """ AUTH PART """
23 |
24 | def get_auth_token(self):
25 | auth_url = "{}/auth".format(self.url)
26 |
27 | creds = {
28 | 'email': self.login,
29 | 'password' : self.password
30 | }
31 |
32 | r = requests.post(auth_url, json=creds)
33 |
34 | token = r.json()['access_token']
35 | auth_string = 'Bearer {}'.format(token)
36 |
37 | return auth_string
38 |
39 | """ SENSORS PART """
40 |
41 | def update_sensor(self, **kwargs):
42 |
43 | url = "{}/sensors/".format(self.url)
44 |
45 | data = json.dumps(kwargs)
46 |
47 | r = requests.post(url,
48 | json=data,
49 | headers=self.headers
50 | )
51 | if r.status_code == 201:
52 | return True
53 | else:
54 | return False
55 |
56 | """ NETWORKS PART """
57 |
58 | def update_network(self, **kwargs):
59 |
60 | url = "{}/networks/".format(self.url)
61 |
62 | data = json.dumps(kwargs)
63 |
64 | r = requests.post(url,
65 | json=data,
66 | headers=self.headers
67 | )
68 | if r.status_code == 201:
69 | return True
70 | else:
71 | return False
72 |
73 | def get_networks(self, **kwargs):
74 | url = "{}/networks/".format(self.url)
75 |
76 | filters = kwargs.get('filter', None)
77 |
78 | if filters:
79 | url += '?filter='+str(filters)
80 |
81 | r = requests.get(url, headers=self.headers)
82 |
83 | if r.status_code == 200:
84 | return AttrDict({'networks': json.loads(r.text)['results']}).networks
85 | else:
86 | return []
87 |
88 | """ HOSTS PART """
89 | # Update / Add host in a network
90 |
91 | def add_host(self, **kwargs):
92 |
93 | network_id = kwargs.get('network_id', None)
94 | url = "{}/networks/{}/".format(self.url, network_id)
95 | data = json.dumps(kwargs)
96 | r = requests.post(url,
97 | json=data,
98 | headers=self.headers
99 | )
100 | if r.status_code == 201:
101 | return True
102 | else:
103 | return False
104 |
105 | def get_macvendor(self, mac_addr):
106 | url = "{}/macvendor/{}/".format(self.url, mac_addr)
107 | r = requests.get(url, headers=self.headers)
108 |
109 | if r.status_code == 200:
110 | return json.loads(r.text)['vendor']
111 | else:
112 | return ""
113 |
--------------------------------------------------------------------------------
/web/src/containers/MainContainer.js:
--------------------------------------------------------------------------------
1 | require('normalize.css/normalize.css');
2 | require('styles/App.css');
3 | // Redux
4 | import React from 'react';
5 |
6 | import { Grid } from 'semantic-ui-react'
7 |
8 | import NavbarComponent from '../components/NavbarComponent.js'
9 | import {connect} from 'react-redux';
10 | import Routes from '../routes';
11 | import { withRouter } from 'react-router-dom'
12 | import { push } from 'react-router-redux'
13 | import PropTypes from 'prop-types';
14 | import { userFetchData } from '../actions/sessions'
15 |
16 | import Notifications from 'react-notification-system-redux';
17 |
18 | class MainContainer extends React.Component {
19 | constructor(props) {
20 | super(props);
21 | }
22 |
23 | componentWillMount() {
24 | // If not logged_in and trying to get another route than /login
25 | if (!this.props.logged_in && ( this.props.router.location.pathname != '/login/') && (!localStorage.jwt)) {
26 | this.props.pushToHistory('/login/');
27 | }
28 | if (this.props.logged_in) {
29 | this.props.userFetchData('/auth/me');
30 | }
31 | }
32 |
33 | componentDidMount() {
34 |
35 | }
36 |
37 | getContent(props) {
38 | var content = ;
39 | if (props.logged_in) {
40 | var content = (
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | );
54 |
55 | };
56 |
57 | return content;
58 | }
59 |
60 | render() {
61 | const { notifications } = this.props;
62 | const style = {
63 | NotificationItem: {
64 | DefaultStyle: {
65 | marginTop: '50px'
66 | }
67 |
68 | }
69 | };
70 |
71 | return (
72 |
73 |
74 |
75 |
79 | {this.getContent(this.props)}
80 |
81 |
82 |
83 | );
84 |
85 | }
86 | }
87 |
88 |
89 | MainContainer.propTypes = {
90 | pushToHistory: PropTypes.func.isRequired,
91 | userFetchData: PropTypes.func.isRequired,
92 | user: PropTypes.object.isRequired,
93 | userHasErrored: PropTypes.bool.isRequired,
94 | userIsLoading: PropTypes.bool.isRequired,
95 | notifications: PropTypes.array
96 | }
97 |
98 | function mapStateToProps(state) {
99 | return {
100 | logged_in: state.sessionReducer,
101 | router: state.router,
102 | user: state.user,
103 | userHasErrored: state.userHasErrored,
104 | userIsLoading: state.userIsLoading,
105 | notifications: state.notifications
106 | };
107 | }
108 |
109 |
110 | const mapDispatchToProps = (dispatch) => {
111 | return {
112 | pushToHistory: (route) => dispatch(push(route)),
113 | userFetchData: (url) => dispatch(userFetchData(url))
114 | };
115 | };
116 |
117 | export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MainContainer));
118 |
119 |
--------------------------------------------------------------------------------
/web/src/lib/api.js:
--------------------------------------------------------------------------------
1 |
2 | export function getApiUrl() {
3 | const ApiUrl = localStorage.getItem('ApiUrl');
4 |
5 | if (ApiUrl !== null) {
6 | return ApiUrl
7 | } else {
8 | return 'localhost:8080'
9 | }
10 |
11 | }
12 | export var ApiUrl = 'http://' + getApiUrl()
13 |
14 | export const forbiddenStatuses = [401, 403, 500]
15 |
16 |
17 | export class sessionApi {
18 |
19 | static login(credentials) {
20 | const request = new Request(ApiUrl + '/auth', {
21 | method: 'POST',
22 | headers: new Headers({
23 | 'Accept': 'application/json',
24 | 'Content-Type': 'application/json, text/plain'
25 | }),
26 | body: JSON.stringify(credentials)
27 |
28 | });
29 |
30 | return fetch(request).then(response => {
31 | if (response.status == 401) {
32 | return {'status': 'failed'}
33 | }
34 | return response.json();
35 | }).catch(error => {
36 | return error;
37 | });
38 | }
39 |
40 |
41 | static verifyToken() {
42 | const request = new Request(ApiUrl + '/auth/verify', {
43 | method: 'GET',
44 | headers: new Headers({
45 | 'Authorization': `Bearer ${localStorage.jwt}`
46 | })
47 | });
48 |
49 | return fetch(request).then(response => {
50 | if (response.status == 400) {
51 | localStorage.removeItem('jwt');
52 | }
53 | return response.json();
54 |
55 | }).catch(error => {
56 | localStorage.removeItem('jwt');
57 | return error;
58 | });
59 | }
60 |
61 | }
62 |
63 |
64 | export class Api {
65 |
66 |
67 | static get(path) {
68 | const request = new Request(ApiUrl + path, {
69 | method: 'GET',
70 | headers: new Headers({
71 | 'Authorization': `Bearer ${localStorage.jwt}`
72 | })
73 | });
74 |
75 | return fetch(request).then(response => {
76 | if (!response.ok) {
77 |
78 | if (forbiddenStatuses.includes(response.status)) {
79 |
80 | sessionApi.verifyToken()
81 | }
82 | throw Error(response.statusText);
83 | }
84 | return response;
85 | }).catch(error => {
86 | return error;
87 | });
88 | }
89 |
90 | static post(path, payload) {
91 | const request = new Request(ApiUrl + path, {
92 | method: 'POST',
93 | headers: new Headers({
94 | 'Authorization': `Bearer ${localStorage.jwt}`
95 | }),
96 | body: JSON.stringify(payload)
97 | });
98 |
99 | return fetch(request).then(response => {
100 | if (!response.ok) {
101 | if (forbiddenStatuses.includes(response.status)) {
102 | sessionApi.verifyToken()
103 | }
104 | throw Error(response.statusText);
105 | }
106 | return response;
107 | }).catch(error => {
108 | return error;
109 | });
110 | }
111 |
112 | static delete(path, payload) {
113 | const request = new Request(ApiUrl + path, {
114 | method: 'DELETE',
115 | headers: new Headers({
116 | 'Authorization': `Bearer ${localStorage.jwt}`
117 | }),
118 | body: JSON.stringify(payload)
119 | });
120 |
121 | return fetch(request).then(response => {
122 | if (!response.ok) {
123 | if (response.status in forbiddenStatuses) {
124 | sessionApi.verifyToken()
125 | }
126 | throw Error(response.statusText);
127 | }
128 | return response;
129 | }).catch(error => {
130 | return error;
131 | });
132 | }
133 |
134 | }
135 |
136 |
--------------------------------------------------------------------------------
/web/src/components/HostDetailComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Grid, Card, Tab, Icon } from 'semantic-ui-react'
5 | import ServicesComponent from './hostdetail/ServicesComponent'
6 | import { formatDate, renderOs } from '../lib/tools';
7 |
8 | require('styles//HostDetail.css');
9 |
10 | class HostDetailComponent extends React.Component {
11 | renderpanes(props) {
12 | const panes = [
13 | {
14 | menuItem: { key: 'services', icon: 'tasks', content: 'Services' },
15 | render: () =>
16 | },
17 | {
18 | menuItem: { key: 'history', icon: 'browser', content: 'History' },
19 | render: () => No history
20 | }
21 | ]
22 | return panes;
23 | }
24 |
25 | render() {
26 |
27 | if (this.props.isLoading) {
28 | const content = Loading...
;
29 | return content;
30 | }
31 | return (
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | Network
41 |
42 |
43 | {this.props.host.host.ip_addr}
44 |
45 |
46 | {this.props.host.host.mac_addr}
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Last seen
61 |
62 |
63 | {formatDate(this.props.host.host.last_seen)}
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | DNS
73 |
74 |
75 | {this.props.host.host.dns_names}
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | Operating system
85 |
86 |
87 | {renderOs(this.props.host)}
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | );
106 | }
107 | }
108 |
109 | HostDetailComponent.displayName = 'HostDetailComponent';
110 |
111 | // Uncomment properties you need
112 | // HostDetailComponent.propTypes = {};
113 | // HostDetailComponent.defaultProps = {};
114 |
115 | export default HostDetailComponent;
116 |
--------------------------------------------------------------------------------
/web/src/containers/SettingsContainer.js:
--------------------------------------------------------------------------------
1 | require('normalize.css/normalize.css');
2 |
3 |
4 | import React from 'react';
5 |
6 | import UserlistComponent from '../components/settings/UserlistComponent'
7 | import SensorListComponent from '../components/settings/SensorListComponent'
8 | import PluginlistComponent from '../components/settings/PluginlistComponent'
9 |
10 | import HeaderComponent from '../components/HeaderComponent'
11 | import BreadcrumbComponent from '../components/BreadcrumbComponent'
12 | import { Tab } from 'semantic-ui-react'
13 |
14 | // Redux
15 | import PropTypes from 'prop-types';
16 | import { connect } from 'react-redux';
17 | import { usersFetchData } from '../actions/users';
18 | import { sensorsFetchData } from '../actions/sensors';
19 | import { pluginsFetchData } from '../actions/plugins';
20 |
21 |
22 | const panes = (props) => [
23 | {
24 | menuItem: { key: 'users', icon: 'users', content: 'Users' },
25 | render: () =>
26 | },
27 | {
28 | menuItem: { key: 'sensors', icon: 'circle outline', content: 'Sensors' },
29 | render: () =>
30 | },
31 | {
32 | menuItem: { key: 'plugins', icon: 'plug', content: 'Plugins' },
33 | render: () =>
34 | }
35 | ]
36 |
37 | class SettingsContainer extends React.Component {
38 | constructor(props) {
39 | super(props);
40 | }
41 |
42 | componentDidMount() {
43 | this.props.usersFetchData('/users/');
44 | this.props.sensorsFetchData('/sensors/');
45 | this.props.pluginsFetchData('/plugins/');
46 | }
47 |
48 | render() {
49 | const links = [
50 | {
51 | 'route': '/settings/',
52 | 'name': 'Settings',
53 | 'active': true
54 | }
55 | ];
56 | return (
57 |
58 |
59 |
64 |
65 |
66 |
67 |
68 |
69 | );
70 | }
71 | }
72 |
73 |
74 | SettingsContainer.propTypes = {
75 | usersFetchData: PropTypes.func.isRequired,
76 | users: PropTypes.object.isRequired,
77 | usershasErrored: PropTypes.bool.isRequired,
78 | usersisLoading: PropTypes.bool.isRequired,
79 | sensorsFetchData: PropTypes.func.isRequired,
80 | sensors: PropTypes.object.isRequired,
81 | sensorshasErrored: PropTypes.bool.isRequired,
82 | sensorsisLoading: PropTypes.bool.isRequired,
83 | plugins: PropTypes.object.isRequired,
84 | pluginshasErrored: PropTypes.bool.isRequired,
85 | pluginsisLoading: PropTypes.bool.isRequired
86 | };
87 |
88 | const mapStateToProps = (state) => {
89 | return {
90 | users: state.users,
91 | usershasErrored: state.usersHasErrored,
92 | usersisLoading: state.usersIsLoading,
93 | sensors: state.sensors,
94 | sensorshasErrored: state.sensorsHasErrored,
95 | sensorsisLoading: state.sensorsIsLoading,
96 | plugins: state.plugins,
97 | pluginshasErrored: state.pluginsHasErrored,
98 | pluginsisLoading: state.pluginsIsLoading
99 | };
100 | };
101 |
102 | const mapDispatchToProps = (dispatch) => {
103 | return {
104 | usersFetchData: (url) => dispatch(usersFetchData(url)),
105 | sensorsFetchData: (url) => dispatch(sensorsFetchData(url)),
106 | pluginsFetchData: (url) => dispatch(pluginsFetchData(url))
107 | };
108 | };
109 |
110 | SettingsContainer.displayName = 'SettingsContainer';
111 |
112 | export default connect(mapStateToProps, mapDispatchToProps)(SettingsContainer);
113 |
--------------------------------------------------------------------------------
/web/src/actions/networks.js:
--------------------------------------------------------------------------------
1 | import { Api } from '../lib/api.js'
2 | import Notifications, { success, error, info } from 'react-notification-system-redux';
3 |
4 | function deleteNetworkNotification(netCount) {
5 | const SuccessDeleteNotification = {
6 | title: 'Network deletion',
7 | message: 'Successfuly deleted ' + netCount + ' network(s)',
8 | position: 'tr',
9 | autoDismiss: 2
10 | };
11 |
12 | return SuccessDeleteNotification
13 | }
14 |
15 | export function networksHasErrored(bool) {
16 | return {
17 | type: 'NETWORKS_HAS_ERRORED',
18 | hasErrored: bool
19 | };
20 | }
21 |
22 | export function networksIsLoading(bool) {
23 | return {
24 | type: 'NETWORKS_IS_LOADING',
25 | isLoading: bool
26 | };
27 | }
28 |
29 | export function networksFetchDataSuccess(networks) {
30 | return {
31 | type: 'NETWORKS_FETCH_DATA_SUCCESS',
32 | networks
33 | };
34 | }
35 |
36 | export function networksFetchData(url) {
37 |
38 | return (dispatch) => {
39 |
40 | dispatch(networksIsLoading(true));
41 |
42 |
43 | Api.get(url)
44 | .then((response) => {
45 | dispatch(networksIsLoading(false));
46 | return response;
47 | })
48 | .then((response) => response.json())
49 | .then((networks) => dispatch(networksFetchDataSuccess(networks)))
50 | .catch(() => dispatch(networksHasErrored(true)));
51 | };
52 | }
53 |
54 | // POST DATA //
55 |
56 | export function networksPostIsLoading(bool) {
57 | return {
58 | type: 'NETWORKS_POST_IS_LOADING',
59 | isLoading: bool
60 | };
61 | }
62 |
63 |
64 | export function networksPostHasErrored(bool) {
65 | return {
66 | type: 'NETWORKS_POST_HAS_ERRORED',
67 | hasErrored: bool
68 | };
69 | }
70 |
71 |
72 | export function networksPostDataSuccess(bool) {
73 | return {
74 | type: 'NETWORKS_POST_DATA_SUCCESS',
75 | isSuccess: bool
76 | };
77 | }
78 |
79 |
80 |
81 | export function networksPostData(url, networks) {
82 |
83 | return (dispatch) => {
84 |
85 | dispatch(networksPostIsLoading(true));
86 |
87 | Api.post(url, networks)
88 | .then((response) => {
89 | dispatch(networksPostIsLoading(false));
90 |
91 | return response;
92 | })
93 | .then((response) => response.json())
94 | .then(dispatch(networksPostDataSuccess(true)))
95 | .catch(() => dispatch(networksPostHasErrored(true)));
96 | };
97 | }
98 |
99 |
100 | // DELETE DATA //
101 |
102 | export function networksDeleteIsLoading(bool) {
103 | return {
104 | type: 'NETWORKS_DELETE_IS_LOADING',
105 | isLoading: bool
106 | };
107 | }
108 |
109 |
110 | export function networksDeleteHasErrored(bool) {
111 | return {
112 | type: 'NETWORKS_DELETE_HAS_ERRORED',
113 | hasErrored: bool
114 | };
115 | }
116 |
117 |
118 | export function networksDeleteDataSuccess(bool) {
119 |
120 | return {
121 | type: 'NETWORKS_DELETE_DATA_SUCCESS',
122 | isSuccess: bool
123 | };
124 | }
125 |
126 |
127 |
128 | export function networksDeleteData(url, networkList) {
129 |
130 | return (dispatch) => {
131 |
132 | dispatch(networksDeleteIsLoading(true));
133 |
134 | Api.delete(url, networkList)
135 | .then((response) => {
136 | dispatch(networksDeleteIsLoading(false));
137 | return response;
138 | })
139 | .then((response) => response.json())
140 | .then(
141 | dispatch(networksDeleteDataSuccess(true)),
142 | dispatch(info(deleteNetworkNotification(networkList.length)))
143 | )
144 | .catch(() => dispatch(networksDeleteHasErrored(true)));
145 | };
146 | }
147 |
--------------------------------------------------------------------------------
/web/src/actions/sessions.js:
--------------------------------------------------------------------------------
1 |
2 | import { sessionApi, Api } from '../lib/api';
3 | import Notifications, { success, error, info } from 'react-notification-system-redux';
4 |
5 | const loginSuccessNotification = {
6 | title: 'Log-in success',
7 | message: 'Welcome',
8 | position: 'tr',
9 | autoDismiss: 2,
10 | };
11 |
12 | const loginFailedNotification = {
13 | title: 'Log-in failed',
14 | message: 'Incorrect username/password',
15 | position: 'tr',
16 | autoDismiss: 2,
17 | };
18 |
19 | const logOutNotification = {
20 | title: 'You logged out',
21 | message: 'See you later !',
22 | position: 'tr',
23 | autoDismiss: 2,
24 | };
25 |
26 | export function loginSuccess(bool, history) {
27 | history.push('/home/');
28 | return {type: 'LOG_IN_SUCCESS'}
29 | }
30 |
31 | export function loginFailed(bool) {
32 | return {
33 | type: 'LOG_IN_FAILED',
34 | isFailed: bool
35 | }
36 | }
37 |
38 | export function loginLoading(bool) {
39 | return {
40 | type: 'LOG_IN_LOADING',
41 | isLoading: bool
42 | }
43 | }
44 |
45 | export function logOutUser(history) {
46 | localStorage.removeItem('jwt');
47 | history.push('/login/');
48 | return {
49 | type: 'LOG_OUT'
50 | }
51 | }
52 |
53 |
54 | export function logInUser(credentials, history) {
55 | return function(dispatch) {
56 |
57 | dispatch(loginLoading(true));
58 |
59 | return sessionApi.login(credentials).then(response => {
60 | console.log(response);
61 | if ('access_token' in response) {
62 |
63 | localStorage.setItem('jwt', response.access_token);
64 | dispatch(loginSuccess(true, history));
65 | dispatch(loginLoading(false));
66 | dispatch(userFetchData('/auth/me'));
67 | dispatch(success(loginSuccessNotification));
68 |
69 | } else {
70 |
71 | dispatch(loginLoading(false));
72 | dispatch(error(loginFailedNotification));
73 | dispatch(loginFailed(true));
74 | }
75 | dispatch(loginLoading(false));
76 |
77 | }).catch(error => {
78 | dispatch(loginLoading(false));
79 | throw(error);
80 | });
81 | };
82 | }
83 |
84 |
85 | // token management
86 |
87 | export function verifyingToken() {
88 | return {
89 | type: 'VERIFYING_TOKEN'
90 | }
91 | }
92 |
93 | export function invalidToken() {
94 | return {
95 | type: 'INVALID_TOKEN'
96 | }
97 | }
98 |
99 |
100 | export function verifyToken() {
101 | return function(dispatch) {
102 |
103 | dispatch(verifyingToken());
104 |
105 | return sessionApi.verifyToken().then(response => {
106 |
107 | if (response.valid == false) {
108 |
109 | localStorage.removeItem('jwt');
110 |
111 | }
112 |
113 | }).catch(error => {
114 |
115 | throw(error);
116 | });
117 | };
118 | }
119 |
120 |
121 |
122 | // user data retrieving
123 |
124 |
125 | export function userHasErrored(bool) {
126 | return {
127 | type: 'USER_HAS_ERRORED',
128 | hasErrored: bool
129 | };
130 | }
131 |
132 | export function userIsLoading(bool) {
133 | return {
134 | type: 'USER_IS_LOADING',
135 | isLoading: bool
136 | };
137 | }
138 |
139 | export function userFetchDataSuccess(user) {
140 | return {
141 | type: 'USER_FETCH_DATA_SUCCESS',
142 | user
143 | };
144 | }
145 |
146 |
147 | export function userFetchData(url) {
148 | return (dispatch) => {
149 | dispatch(userIsLoading(true));
150 |
151 | Api.get(url)
152 | .then((response) => {
153 | if (!response.ok) {
154 | throw Error(response.statusText);
155 | }
156 |
157 | dispatch(userIsLoading(false));
158 |
159 | return response;
160 | })
161 | .then((response) => response.json())
162 | .then((user) => dispatch(userFetchDataSuccess(user)))
163 | .catch(() => dispatch(userHasErrored(true)));
164 | };
165 | }
--------------------------------------------------------------------------------
/web/src/components/modals/NewNetworkComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Button, Modal, Icon, Form } from 'semantic-ui-react'
5 | import {ValidateIPaddress} from '../../lib/tools';
6 |
7 | // Redux
8 | import PropTypes from 'prop-types';
9 | import { connect } from 'react-redux';
10 | import { networksPostData } from '../../actions/networks';
11 |
12 | class NewNetworkComponent extends React.Component {
13 | constructor(props) {
14 | super(props);
15 | this.handleChangeName = this.handleChangeName.bind(this);
16 | this.handleChangeIp = this.handleChangeIp.bind(this);
17 | this.handleOk = this.handleOk.bind(this);
18 | this.handleKeyPress = this.handleKeyPress.bind(this);
19 | this.state = {
20 | modalOpen: false,
21 | name: '',
22 | ip_addr: ''
23 | };
24 | }
25 |
26 | handleOpen = () => this.setState({ modalOpen: true })
27 |
28 | handleClose = () => this.setState({ modalOpen: false })
29 |
30 |
31 | handleChangeName(event) {
32 | this.setState({name: event.target.value});
33 | }
34 |
35 | handleChangeIp(event) {
36 | this.setState({ip_addr: event.target.value});
37 | }
38 |
39 | handleOk() {
40 | if (ValidateIPaddress(this.state.ip_addr)) {
41 | this.props.networksPostData('/networks/', this.state)
42 | this.props.fetchData('/networks/');
43 | this.handleClose();
44 | }
45 | }
46 |
47 | handleKeyPress = (event) => {
48 | if(event.key == 'Enter'){
49 | this.handleOk(event);
50 | }
51 | }
52 |
53 | render() {
54 | return (
55 | }
65 | open={this.state.modalOpen}
66 | onClose={this.handleClose}
67 | size='small'>
68 | New network
69 |
70 |
72 |
79 |
86 |
87 |
88 |
89 |
90 |
93 |
102 |
103 |
104 | );
105 | }
106 | }
107 |
108 | NewNetworkComponent.displayName = 'ModalsNewNetworkComponent';
109 |
110 | NewNetworkComponent.propTypes = {
111 | show: PropTypes.bool,
112 | children: PropTypes.node,
113 | networksPostData: PropTypes.func.isRequired,
114 | hasErrored: PropTypes.bool.isRequired,
115 | isLoading: PropTypes.bool.isRequired
116 | };
117 |
118 |
119 |
120 | const mapStateToProps = (state) => {
121 | return {
122 | hasErrored: state.networksHasErrored,
123 | isLoading: state.networksIsLoading,
124 | isSuccess: state.networksIsSuccess
125 | };
126 | };
127 |
128 | const mapDispatchToProps = (dispatch) => {
129 | return {
130 | networksPostData: (url, networks) => dispatch(networksPostData(url, networks))
131 | };
132 | };
133 |
134 | NewNetworkComponent.displayName = 'NewNetworkComponent';
135 |
136 | export default connect(mapStateToProps, mapDispatchToProps)(NewNetworkComponent);
--------------------------------------------------------------------------------
/web/src/components/LoginPageComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Button, Form, Grid, Header, Segment, Checkbox, Transition,Input } from 'semantic-ui-react'
5 | import {bindActionCreators} from 'redux';
6 | import {connect} from 'react-redux';
7 | import * as sessionActions from '../actions/sessions';
8 |
9 | import { Redirect } from 'react-router'
10 | import { getApiUrl } from '../lib/api'
11 |
12 | require('styles//LoginPage.css');
13 | let logoImage = require('../images/logo.png');
14 |
15 | class LoginPageComponent extends React.Component {
16 |
17 |
18 | constructor(props) {
19 | super(props);
20 | this.state = {credentials: {email: '', password: ''}}
21 | this.onChange = this.onChange.bind(this);
22 | this.onSave = this.onSave.bind(this);
23 | }
24 |
25 | onChange(event) {
26 | const field = event.target.name;
27 | const credentials = this.state.credentials;
28 | credentials[field] = event.target.value;
29 | return this.setState({credentials: credentials});
30 | }
31 |
32 | onSave(event) {
33 | event.preventDefault();
34 | this.props.actions.logInUser(this.state.credentials, this.props.history);
35 | }
36 |
37 | updateApiUrl(evt) {
38 | localStorage.setItem('ApiUrl', evt.target.value);
39 | }
40 |
41 | render() {
42 | if (!this.props.logged_in) {
43 |
44 | return (
45 |
46 |
123 |
124 | );
125 | } else {
126 | return ( );
127 | }
128 |
129 | }
130 | }
131 |
132 | LoginPageComponent.displayName = 'LoginPageComponent';
133 |
134 | function mapStateToProps(state) {
135 | return {
136 | logged_in: state.sessionReducer,
137 | loginLoading: state.loginLoading
138 | };
139 | }
140 |
141 |
142 | function mapDispatchToProps(dispatch) {
143 | return {
144 | actions: bindActionCreators(sessionActions, dispatch)
145 | };
146 | }
147 |
148 |
149 |
150 | export default connect(mapStateToProps, mapDispatchToProps)(LoginPageComponent);
--------------------------------------------------------------------------------
/web/src/components/NetworkListComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 |
5 | import { Icon, Table, Button, Confirm } from 'semantic-ui-react';
6 | import NewNetworkComponent from './modals/NewNetworkComponent';
7 |
8 | require('styles//NetworkList.css');
9 |
10 |
11 | class NetworkListComponent extends React.Component {
12 | constructor(props) {
13 | super(props);
14 | this.state = {
15 | selectedNetworks: [],
16 | confirmDeleteOpen: false,
17 | result: ''
18 | }
19 | }
20 |
21 | componentWillUpdate(nextProps, nextState) {
22 | if (nextState.result == 'confirmed' && this.state.result != 'confirmed') {
23 | this.props.networksDeleteData('/networks/', this.state.selectedNetworks);
24 | this.setState({selectedNetworks: []});
25 | this.props.fetchData('/networks/');
26 | }
27 | }
28 |
29 | showConfirm = () => this.setState({ confirmDeleteOpen: true })
30 | handleConfirm = () => this.setState({ result: 'confirmed', confirmDeleteOpen: false })
31 | handleConfirmCancel = () => this.setState({ result: 'cancelled', confirmDeleteOpen: false })
32 |
33 | onChange(e) {
34 | const selectedNetworks = this.state.selectedNetworks
35 | let index
36 | if (e.target.checked) {
37 |
38 | selectedNetworks.push(e.target.value)
39 | } else {
40 |
41 | index = selectedNetworks.indexOf(e.target.value)
42 | selectedNetworks.splice(index, 1)
43 | }
44 |
45 | this.setState({ selectedNetworks: selectedNetworks, result: '' })
46 | }
47 |
48 | selectedNetworksCount() {
49 | const result = ' Delete selected ( ' + this.state.selectedNetworks.length + ' )';
50 | return result
51 | }
52 |
53 | render() {
54 | let content;
55 |
56 | if (this.props.hasErrored) {
57 | content = API Unavailable
;
58 | return content;
59 | }
60 |
61 | return (
62 |
128 | );
129 | }
130 | }
131 |
132 | NetworkListComponent.propTypes = {
133 |
134 | };
135 |
136 |
137 | NetworkListComponent.displayName = 'NetworkListComponent';
138 |
139 | export default NetworkListComponent;
140 |
--------------------------------------------------------------------------------
/web/src/components/NavbarComponent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Input, Menu, Icon, Dropdown } from 'semantic-ui-react'
5 | import PropTypes from 'prop-types';
6 |
7 | // Redux
8 | import { connect } from 'react-redux';
9 | import { push } from 'react-router-redux'
10 | import { setSearchTerm } from '../actions/search';
11 | import { searchFetchData } from '../actions/search';
12 | import { withRouter } from 'react-router-dom'
13 | import { logOutUser } from '../actions/sessions';
14 |
15 | require('styles//Navbar.css');
16 |
17 |
18 | class NavbarComponent extends React.Component {
19 | constructor(props) {
20 | super(props);
21 | this.logOut = this.logOut.bind(this);
22 | }
23 |
24 | state = { page: '' }
25 |
26 | componentDidMount() {
27 | const route = this.props.router.location.pathname;
28 | const page = route.split('/');
29 |
30 | this.setState({ page: page[1] });
31 | this.searchinput.focus();
32 | }
33 |
34 | logOut(event) {
35 | event.preventDefault();
36 | this.props.logOutUser(this.props.history);
37 | }
38 |
39 | navigate(name) {
40 | this.props.pushToHistory('/' + name + '/');
41 | this.setState({ page: name });
42 | }
43 |
44 | handleClick = (e, { name }) => this.navigate(name)
45 |
46 | searchOnChange(e, { value }) {
47 | const route = this.props.router.location.pathname;
48 | this.props.setSearchTerm(value);
49 | this.props.fetchData('/search?q=' + value);
50 |
51 | if (route != '/search/'){
52 | this.navigate('search');
53 | }
54 |
55 | }
56 |
57 | // Set cursor position to the end
58 | moveCaretAtEnd(e) {
59 | var temp_value = e.target.value
60 | e.target.value = ''
61 | e.target.value = temp_value
62 | }
63 |
64 | render() {
65 |
66 | const { page } = this.state;
67 |
68 | return (
69 |
70 |
121 |
122 | );
123 | }
124 | }
125 |
126 |
127 |
128 |
129 |
130 | NavbarComponent.propTypes = {
131 | pushToHistory: PropTypes.func.isRequired,
132 | fetchData: PropTypes.func.isRequired,
133 | search: PropTypes.object.isRequired,
134 | hasErrored: PropTypes.bool.isRequired,
135 | isLoading: PropTypes.bool.isRequired,
136 | setSearchTerm: PropTypes.func.isRequired,
137 | searchterm: PropTypes.string.isRequired,
138 | logged_in: PropTypes.bool.isRequired,
139 | userIsLoading: PropTypes.bool.isRequired
140 | };
141 |
142 | const mapStateToProps = (state) => {
143 | return {
144 | search: state.search,
145 | hasErrored: state.searchHasErrored,
146 | isLoading: state.searchIsLoading,
147 | searchterm: state.searchterm,
148 | router: state.router,
149 | logged_in: state.sessionReducer,
150 | userIsLoading: state.userIsLoading
151 | };
152 | };
153 |
154 | const mapDispatchToProps = (dispatch) => {
155 | return {
156 | fetchData: (url) => dispatch(searchFetchData(url)),
157 | setSearchTerm: (term) => dispatch(setSearchTerm(term)),
158 | pushToHistory: (route) => dispatch(push(route)),
159 | logOutUser: (history) => dispatch(logOutUser(history))
160 | };
161 | };
162 |
163 | NavbarComponent.displayName = 'NavbarComponent';
164 |
165 | export default withRouter(connect(mapStateToProps, mapDispatchToProps)(NavbarComponent));
166 |
--------------------------------------------------------------------------------
/shared/iptools.py:
--------------------------------------------------------------------------------
1 | import logging
2 | logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
3 |
4 | import os, sys
5 | import netifaces as ni
6 | from attrdict import AttrDict
7 | from ipaddress import ip_address, ip_network, IPv4Network, IPv4Interface
8 | from scapy.all import srp, Ether, ARP, conf
9 | from queue import Queue
10 | import threading
11 | from shared.iftools import sensor_ifaces
12 |
13 | import nmap
14 | try:
15 | nm = nmap.PortScanner()
16 | except:
17 | print('Nmap not found', sys.exc_info()[0])
18 | sys.exit(0)
19 |
20 |
21 |
22 | conf.verbose = 0
23 |
24 | # Check if an ip is in the specified range
25 |
26 |
27 | def in_network(ip_addr, network):
28 | result = False
29 | if ip_address(ip_addr) in ip_network(network, strict=False):
30 | result = True
31 |
32 | return result
33 |
34 | def get_network(ip_addr, netmask):
35 | result = ip_addr
36 |
37 | interface = IPv4Interface(ip_addr+'/'+netmask)
38 | network = interface.network
39 |
40 | network = {
41 | 'name': 'NET_' + str(network),
42 | 'ip_addr': str(network)
43 | }
44 |
45 | return network
46 |
47 |
48 | # Local IP -> MAC
49 |
50 | def local_mac_from_ip(ip):
51 | for i in ni.interfaces():
52 | addrs = ni.ifaddresses(i)
53 | try:
54 | if_mac = addrs[ni.AF_LINK][0]['addr']
55 | if_ip = addrs[ni.AF_INET][0]['addr']
56 | except:
57 | if_mac = if_ip = None
58 |
59 | if if_ip == ip:
60 | return if_mac
61 | return ""
62 |
63 | # Quiet ARP Scanner
64 |
65 |
66 | def arp_scan(iface, network):
67 | results = []
68 | alive, dead = srp(Ether(dst="ff:ff:ff:ff:ff:ff") /
69 | ARP(pdst=network), timeout=2, verbose=False, iface=iface)
70 | for i in range(0, len(alive)):
71 | mac = alive[i][1].hwsrc
72 | ip = alive[i][1].psrc
73 |
74 | cur_host = {
75 | "ip_addr": ip,
76 | "mac_addr": mac
77 | }
78 |
79 | results.append(AttrDict(cur_host))
80 |
81 | # Adding the local sensor to the list
82 | for addr in sensor_ifaces():
83 | if hasattr(addr, 'ip_addr'):
84 | cur_addr = addr['ip_addr']
85 | if in_network(cur_addr, network):
86 | cur_mac = local_mac_from_ip(cur_addr)
87 | for host in results:
88 | if host['ip_addr'] == cur_addr:
89 | host['mac_addr'] = cur_mac
90 |
91 | local_host = AttrDict(
92 | {"ip_addr": cur_addr, "mac_addr": cur_mac})
93 | if not local_host in results:
94 | results.append(local_host)
95 |
96 | return results
97 |
98 |
99 | def ping(ip):
100 | return os.system("ping -c 1 " + ip + " >/dev/null 2>&1") == 0
101 |
102 |
103 | def arping(iface, ip):
104 | result = ""
105 | ans, uans = srp(Ether(dst="FF:FF:FF:FF:FF:FF")/ARP(pdst=ip),
106 | timeout=2, inter=0.1, verbose=False, iface=iface)
107 | if len(ans) > 0:
108 | for snd, rcv in ans:
109 | result = rcv.sprintf(r"%Ether.src%")
110 |
111 | return result
112 |
113 |
114 | def ping_scan(iface, network):
115 |
116 | input_queue = Queue()
117 | results = []
118 |
119 | class PingHostThread(threading.Thread):
120 |
121 | def __init__(self, iface, input_queue, results):
122 | super().__init__()
123 | self.input_queue = input_queue
124 | self.results = results
125 | self.iface = iface
126 |
127 | def run(self):
128 | while True:
129 | ip = self.input_queue.get()
130 | is_alive = ping(ip)
131 | if is_alive:
132 | mac_addr = arping(self.iface, ip)
133 | if len(mac_addr) > 0:
134 | cur_host = {
135 | "ip_addr": ip,
136 | "mac_addr": mac_addr
137 | }
138 |
139 | self.results.append(AttrDict(cur_host))
140 | self.input_queue.task_done()
141 |
142 | # Start 20 Threads, all are waiting in run -> self.input_queue.get()
143 | for i in range(32):
144 | thread = PingHostThread(iface, input_queue, results)
145 | thread.setDaemon(True)
146 | thread.start()
147 |
148 | # Fill the input queue
149 | for addr in IPv4Network(network, strict=False):
150 | input_queue.put(str(addr))
151 |
152 | input_queue.join()
153 |
154 | return results
155 |
156 |
157 |
158 | def nmap_host(host, **kwargs):
159 |
160 | tcp_port_range = kwargs.get('tcp_port_range', '22-443')
161 |
162 | services = []
163 | details = {}
164 | ip_addr = host.ip_addr
165 |
166 | nm.scan(ip_addr, tcp_port_range,
167 | arguments='-sS -sU --max-retries 1 -PN -n -O')
168 |
169 | """ There is a better way to do that """
170 | for proto in nm[ip_addr].all_protocols():
171 |
172 | proto_dict = {
173 | 'name': proto,
174 | 'ports': []
175 | }
176 |
177 | ports = []
178 | for port in list(nm[ip_addr][proto].keys()):
179 | if nm[ip_addr][proto][port]['state'] == 'open':
180 | if not port in ports:
181 | ports.append(port)
182 |
183 | proto_dict['ports'] = ports
184 |
185 | if not proto_dict in services:
186 | services.append(proto_dict)
187 |
188 | for l in nm[ip_addr]['osmatch']:
189 |
190 | for os in l['osclass']:
191 |
192 | accuracy = int(os['accuracy'])
193 | osfamily = os['osfamily']
194 | if osfamily != None and accuracy >= 90:
195 | details['osfamily'] = osfamily
196 | details['osgen'] = os['osgen']
197 |
198 | host.services = services
199 | host.details = details
200 |
201 | return host
202 |
203 |
204 |
205 | if __name__ == "__main__":
206 | # print(ping_scan("192.168.56.0/24"))
207 | print(arping("vboxnet0", "192.168.56.1"))
208 | print(ping_scan('vboxnet0', '192.168.56.0/24'))
209 | print(nmap_host('192.168.40.254'))
210 |
--------------------------------------------------------------------------------
/core/database.py:
--------------------------------------------------------------------------------
1 |
2 | import json
3 | from shared.hashtools import make_shortuuid
4 | import rethinkdb as r
5 | import rethinkdb.errors
6 | from config import db_host, db_port, db_name
7 |
8 | tableList = ['hosts', 'networks', 'sensors', 'users']
9 |
10 | """ INIT DATABASE """
11 |
12 |
13 | def make_connection(dbhost, dbport, **kwargs):
14 | conn = r.connect(dbhost, dbport, **kwargs).repl()
15 | return conn
16 |
17 |
18 | def create_database(conn, dbname):
19 | try:
20 | r.db_create(dbname).run(conn)
21 | except rethinkdb.errors.ReqlOpFailedError:
22 | pass
23 | return True
24 |
25 |
26 | def create_table(conn, dbname, tablename):
27 | try:
28 | r.db(dbname).table_create(tablename).run(conn)
29 | except rethinkdb.errors.ReqlOpFailedError:
30 | pass
31 | return True
32 |
33 |
34 | def init_database(dbhost, dbport, dbname, tableList):
35 | conn = make_connection(dbhost, dbport)
36 | create_database(conn, dbname)
37 |
38 | for table in tableList:
39 | create_table(conn, dbname, table)
40 |
41 | conn = make_connection(dbhost, dbport, db=dbname)
42 | return conn
43 |
44 | setup_db = init_database(db_host, db_port, db_name, tableList)
45 |
46 | conn = make_connection(db_host, db_port, db=db_name)
47 |
48 |
49 |
50 | """ INSERT/UPDATE NETWORK """
51 |
52 |
53 |
54 | def update_network(**kwargs):
55 |
56 | """ Network id is based on network name """
57 |
58 | ip_addr = kwargs.get('ip_addr')
59 | is_private = kwargs.get('is_private')
60 | name = kwargs.get('name')
61 | dns_names = kwargs.get('dns_names')
62 | is_scanning = kwargs.get('is_scanning', False)
63 | network_id = make_shortuuid(name)
64 |
65 | network = {
66 | 'dns_names': dns_names,
67 | 'ip_addr': ip_addr,
68 | 'is_private' : is_private,
69 | 'name': name,
70 | 'id': network_id,
71 | 'is_scanning': is_scanning,
72 | 'updated_count': 0
73 |
74 | }
75 |
76 | network_exists = r.table("networks").insert([network], conflict="update")
77 |
78 | return network_exists.run(conn)
79 |
80 |
81 | def delete_network(**kwargs):
82 |
83 | network_id = kwargs.get('network_id')
84 |
85 | network = r.table("networks").filter({'id': network_id})
86 |
87 | return network.delete().run(conn)
88 |
89 | """ INSERT NEW HOST """
90 | """ Host_id is based on mac_address"""
91 |
92 |
93 | def new_host(**kwargs):
94 |
95 | mac_addr = kwargs.get('mac_addr')
96 |
97 | host = kwargs
98 | host['id'] = make_shortuuid(mac_addr)
99 |
100 | r.table("hosts").insert([host], conflict="update").run(conn)
101 |
102 | return True
103 |
104 |
105 | """ GET ALL NETWORKS """
106 |
107 |
108 | def get_networks(**kwargs):
109 |
110 | network_id = kwargs.get('network_id')
111 | ip_addr = kwargs.get('ip_addr', False)
112 |
113 | networks = r.table('networks')
114 |
115 | if network_id:
116 | networks = networks.filter({'id': network_id})
117 |
118 | if ip_addr:
119 | networks = networks.filter({'ip_addr': ip_addr})
120 |
121 | networks = networks.order_by(r.desc('name')).run(conn)
122 |
123 |
124 | return list(networks)
125 |
126 |
127 | """ GET ALL HOSTS FROM THE SPECIFIED NETWORK ID"""
128 |
129 |
130 | def get_hosts(**kwargs):
131 |
132 |
133 | network_id = kwargs.get('network_id')
134 | text_query = kwargs.get('text_query')
135 | host_id = kwargs.get('host_id')
136 |
137 | hosts = r.table('hosts')
138 |
139 | if network_id:
140 | hosts = hosts.filter({'network_id': network_id})
141 |
142 | if host_id:
143 | hosts = hosts.filter({'id': host_id})
144 |
145 | if text_query:
146 | hosts = hosts.filter(
147 | lambda doc: (doc['dns_names'].match(text_query)) | (
148 | doc['ip_addr'].match(text_query))
149 |
150 | )
151 |
152 | hosts = hosts.order_by('ip_addr').run(conn)
153 |
154 | return list(hosts)
155 |
156 |
157 | """ SENSORS PART """
158 | """ GET ALL SENSORS"""
159 |
160 | def get_sensors(**kwargs):
161 |
162 |
163 | sensors = r.table('sensors').run(conn)
164 |
165 | return list(sensors)
166 |
167 |
168 | """ INSERT/UPDATE SENSOR """
169 |
170 |
171 | def update_sensor(**kwargs):
172 |
173 | ip_addr = kwargs.get('ip_addr')
174 | is_private = kwargs.get('is_private')
175 | name = kwargs.get('name')
176 | dns_names = kwargs.get('dns_names')
177 | interfaces = kwargs.get('interfaces', [])
178 |
179 | sensor = {
180 |
181 | 'dns_names': dns_names,
182 | 'ip_addr': ip_addr,
183 | 'is_private' : is_private,
184 | 'name': name,
185 | 'id': make_shortuuid(name),
186 | 'interfaces': interfaces
187 |
188 | }
189 |
190 | r.table("sensors").insert([sensor], conflict="update").run(conn)
191 |
192 | return True
193 |
194 |
195 | """" USERS PART """
196 |
197 | def get_user(**kwargs):
198 | email = kwargs.get('email')
199 | user_id = kwargs.get('user_id')
200 |
201 | users = r.table('users')
202 |
203 | if email:
204 | users = users.filter({'email': email})
205 |
206 | if user_id:
207 | users = users.filter({'id': user_id})
208 |
209 | users = users.run(conn)
210 | user = list(users)
211 |
212 | if len(user)>0:
213 | user = user[0]
214 | else:
215 | user = None
216 |
217 | return user
218 |
219 |
220 | def get_users(**kwargs):
221 | email = kwargs.get('email')
222 | user_id = kwargs.get('user_id')
223 |
224 | users = r.table('users')
225 |
226 | if email:
227 | users = users.filter({'email': email})
228 |
229 | if user_id:
230 | users = users.filter({'id': user_id})
231 |
232 | users = users.run(conn, read_mode="outdated")
233 |
234 | return list(users)
235 |
236 |
237 | def add_user(**kwargs):
238 |
239 | email = kwargs.get('email')
240 | password = kwargs.get('password')
241 |
242 | user = {
243 |
244 | 'email': email,
245 | 'password': password,
246 | }
247 |
248 | user_exists = get_users(email=email)
249 | if len(user_exists) == 0:
250 | return r.table("users").insert([user], conflict="update").run(conn)
251 |
252 | return True
253 |
254 |
255 |
256 | def server_status():
257 | if r.db('rethinkdb').table('server_status').run(conn):
258 | return True
259 | else:
260 | return False
261 |
--------------------------------------------------------------------------------
/sensor/sensor.py:
--------------------------------------------------------------------------------
1 |
2 | import sys
3 | import logging
4 | import argparse
5 | from queue import Queue
6 | from attrdict import AttrDict
7 | from shared import api_wrapper as apiwr
8 | from shared import console
9 | from shared.iptools import arp_scan, ping_scan, nmap_host, get_network
10 | from shared.dnstools import reverse_lookup, get_domain_name
11 | from shared.iftools import which_iface, sensor_ifaces
12 | from attrdict import AttrDict
13 | from datetime import datetime
14 | from config import api_url, sensor_name, tcp_port_range, login, password
15 |
16 |
17 | parser = argparse.ArgumentParser()
18 | parser.add_argument('--no-portscan', help='disable port scan', action="store_true")
19 | parser.add_argument('--no-icmp', help='disable ICMP scan', action="store_true")
20 | parser.add_argument('--filter', help='Scan only selected network')
21 | parser.add_argument('--show-networks', help='Display all networks provided by the API', action="store_true")
22 | parser.add_argument('--discover', help='Auto-detect networks', action="store_true")
23 |
24 | args = parser.parse_args()
25 |
26 |
27 | ''' Sensor object '''
28 |
29 | class Sensor(object):
30 | """ Sensor object """
31 |
32 | def __init__(self, api_url, **kwargs):
33 | self.api = self.connect(api_url)
34 | self.interfaces = sensor_ifaces()
35 | self.portscan = kwargs.get('portscan', True)
36 | self.icmpscan = kwargs.get('icmpscan', True)
37 | self.filter = kwargs.get('filter', None)
38 | self.networks = self.api.get_networks(filter=self.filter)
39 | self.local_networks = []
40 | self.update()
41 |
42 | def connect(self, api_url):
43 | """ Connection to the api with the api_wrapper lib """
44 |
45 | print('\n------------------------------')
46 | try:
47 | console.write('Checking API connectivity at ' + api_url + "...", 3)
48 | api = apiwr.CoreApi(
49 | url=api_url,
50 | login=login,
51 | password=password
52 | )
53 | console.write('Successfully connected to ' + api_url + "...", 0)
54 | print('------------------------------\n')
55 |
56 | except BaseException:
57 | console.write('Unable to communicate with the API at ' + api_url, 2)
58 | print('------------------------------\n')
59 | sys.exit(2)
60 |
61 | return api
62 |
63 |
64 | def update(self):
65 | self.api.update_sensor(ip_addr=self.interfaces[0].ip_addr,
66 | name=sensor_name,
67 | dns_names='sensor1.local',
68 | interfaces=self.interfaces)
69 |
70 |
71 | def full_scan(self, network):
72 | network_ip = network['ip_addr']
73 |
74 | # Choose interface for performing the scan
75 | interface = which_iface(network_ip)
76 | console.write('Using interface: {}'.format(interface), 3)
77 |
78 | updated_network = AttrDict({
79 | "hosts": [],
80 | "dns_names": ""
81 | })
82 |
83 | hosts = []
84 |
85 | # 1 - do arp scan
86 | console.write('Performing ARP Scan', 3)
87 | arphosts = arp_scan(interface, network_ip)
88 | if len(arphosts) > 0:
89 | console.write('Found {} Hosts'.format(len(arphosts)), 4)
90 |
91 | # 2 - do icmp scan
92 | # Fusion the two objects (pingscan[] and arpscan[])
93 |
94 | pinghosts = []
95 | if self.icmpscan:
96 | console.write('Performing ICMP Scan', 3)
97 | pinghosts = ping_scan(interface, network_ip)
98 |
99 | hosts = {x['mac_addr']: x for x in arphosts + pinghosts}.values()
100 |
101 | # Resolve each found host threaded ?
102 | for host in hosts:
103 |
104 | # Get current date
105 | host.last_seen = datetime.now().isoformat()
106 |
107 | """ dns """
108 | host.dns_names = reverse_lookup(host.ip_addr)
109 | console.write('Performing Reverse lookup on {} -> {}'.format(host.ip_addr, host.dns_names), 3)
110 | """"""
111 |
112 | """ PORT SCAN (uses nmap) """
113 | host.services = []
114 | if self.portscan:
115 | msg = 'Performing Service discovery on {} / TCP: {}'.format(str(host.ip_addr), tcp_port_range)
116 | console.write(msg, 3)
117 | host = nmap_host(
118 | host, port_range=tcp_port_range)
119 | """"""
120 |
121 | """ Get mac vendor """
122 | macvendor = self.api.get_macvendor(host.mac_addr)
123 | console.write(
124 | "Resolving MAC vendor for {} -> {}".format(host.ip_addr, macvendor), 3)
125 | host['mac_vendor'] = macvendor
126 | """"""
127 |
128 |
129 | host['network_id'] = network['id']
130 |
131 | self.api.add_host(**host)
132 |
133 | # Get the network domain name if possible
134 | if hasattr(updated_network, 'dns_names'):
135 | if '.' in host.dns_names:
136 | domain_name = get_domain_name(host.dns_names)
137 | if len(domain_name) > len(updated_network['dns_names']):
138 | updated_network.dns_names = domain_name
139 |
140 | updated_network['hosts'].append(host)
141 |
142 | return updated_network
143 |
144 |
145 | def main():
146 | """ Main program """
147 | sensor = Sensor(api_url,
148 | portscan=(not args.no_portscan),
149 | icmpscan=(not args.no_icmp),
150 | filter=args.filter)
151 |
152 | returned_networks = len(sensor.networks)
153 | if returned_networks == 0:
154 | msg = "No networks were returned by the API. You can add networks on the web interface."
155 | console.write(msg, 3)
156 |
157 | sys.exit(0)
158 |
159 | api = sensor.api
160 |
161 | if args.no_icmp:
162 | console.write('ICMP scan disabled', 3)
163 |
164 | if args.no_portscan:
165 | console.write('Port scan disabled', 3)
166 |
167 | if args.discover:
168 |
169 | net_count = 0
170 |
171 | for i in sensor.interfaces:
172 | network = get_network(i.ip_addr, i.netmask)
173 | api.update_network(**network)
174 | msg = 'Detected {} - {}'.format(network['name'],
175 | network['ip_addr'])
176 | console.write(msg, 3)
177 | net_count += 1
178 |
179 | msg = 'Added/updated {} network(s) to the api'.format(net_count)
180 | console.write(msg, 0)
181 | sys.exit(0)
182 |
183 | if args.show_networks:
184 | console.write('Network list:', 3)
185 | for network in sensor.networks:
186 | print(network['name'], network['ip_addr'])
187 | sys.exit(0)
188 |
189 | # Retreive networks from the API
190 | for network in sensor.networks:
191 |
192 | start = datetime.now()
193 |
194 | msg = "Starting scan on: {} - {}".format(
195 | network['name'], network['ip_addr'])
196 |
197 | console.write(msg, 3)
198 |
199 | network['is_scanning'] = True
200 | api.update_network(**network)
201 | scan_result = sensor.full_scan(network)
202 |
203 | network['is_scanning'] = False
204 | network['dns_names'] = scan_result.dns_names
205 |
206 | api.update_network(**network)
207 |
208 | hosts = scan_result.hosts
209 |
210 | end = datetime.now()
211 | elapsed = (end - start) / 60
212 |
213 | console.write("Scan finnished, {} host(s) found. Elapsed time: {}".format(
214 | len(hosts), elapsed), 0)
215 | print('\n------------------------------')
216 |
217 | if __name__ == "__main__":
218 | main()
219 |
--------------------------------------------------------------------------------
/core/api.py:
--------------------------------------------------------------------------------
1 |
2 | import os
3 | import sys
4 | import logging
5 | from config import debug, production
6 | from sanic import Sanic
7 | from sanic import response
8 | from sanic_cors import CORS
9 |
10 | # Blueprints for swagger ( REMOVE IN PRODUCTION MODE)
11 | if not production:
12 | from sanic_openapi import swagger_blueprint, openapi_blueprint
13 |
14 | from sanic_openapi import doc
15 |
16 | import json
17 | import database as db
18 | from shared import mactools
19 | from time import sleep
20 | from shared.mltools import is_gateway
21 | from shared.console import write
22 | from utils.utils import is_private, list_plugins
23 |
24 | from sanic_jwt import initialize
25 | from sanic_jwt import exceptions
26 | from sanic_jwt.decorators import protected
27 |
28 |
29 | # Get script paths
30 | pathname = os.path.dirname(sys.argv[0])
31 | fullpath = os.path.abspath(pathname)
32 |
33 | """ DEFINE THE APP """
34 |
35 | app = Sanic(strict_slashes=True)
36 |
37 | """ OPENAPI/ SWAGGER BLUEPRINTS """
38 |
39 | if not production:
40 | # Blueprints for swagger ( REMOVE IN PRODUCTION MODE)
41 | app.blueprint(openapi_blueprint)
42 | app.blueprint(swagger_blueprint)
43 |
44 |
45 | """ AUTH FUNCTIONS """
46 |
47 | async def authenticate(request, *args, **kwargs):
48 | """ This function is used for checking
49 | user/password from the database """
50 | user = None
51 |
52 | email = request.json.get('email', None)
53 | password = request.json.get('password', None)
54 |
55 | if not email or not password:
56 | raise exceptions.AuthenticationFailed("Missing email or password.")
57 |
58 | user = db.get_user(email=email)
59 |
60 | if (user is None) or (password != user['password']):
61 | raise exceptions.AuthenticationFailed("Incorrect user/password")
62 |
63 | return user
64 |
65 |
66 | def retrieve_user(request, payload, *args, **kwargs):
67 | if payload:
68 | user_id = payload.get('user_id', None)
69 | user = db.get_user(user_id=user_id)
70 |
71 | return user
72 | else:
73 | return None
74 |
75 | initialize(app,
76 | authenticate,
77 | retrieve_user=retrieve_user
78 | )
79 |
80 |
81 | """ CORS """
82 |
83 | cors = CORS(app, resources={r"*": {"origins": "*"}})
84 |
85 | @doc.summary("Returns route METHODS")
86 | @app.middleware('request')
87 | async def method_interception(request):
88 | if request.method == 'OPTIONS':
89 | return response.json(
90 | {},
91 | headers={'Access-Control-Max-Age': 3600},
92 | status=200
93 | )
94 |
95 |
96 | """ HOSTS """
97 |
98 | def count_ports(host):
99 | svc_count = 0
100 |
101 | for proto in host['services']:
102 | for port in proto['ports']:
103 | svc_count += 1
104 |
105 | host['open_ports'] = svc_count
106 |
107 | return host
108 |
109 | @doc.summary("Show host details")
110 | @app.route("/hosts//", methods=['GET'])
111 | @protected()
112 | async def get_host(request, host_id):
113 |
114 | results = {
115 | "host": {
116 | }
117 | }
118 |
119 | hosts = db.get_hosts(host_id=host_id)
120 |
121 | if len(hosts) > 0:
122 | host = hosts[0]
123 | host = count_ports(host)
124 |
125 | results['host'] = host
126 | results['host']['network'] = db.get_networks(
127 | network_id=host['network_id'])[0]
128 |
129 | return response.json(results)
130 |
131 |
132 | """ NETWORKS """
133 |
134 |
135 | @doc.summary("Returns all discovered networks")
136 | @app.route("/networks/", methods=['GET'])
137 | @protected()
138 | async def get_networks(request):
139 |
140 | results = {
141 | "results": []
142 | }
143 |
144 | args = request.args
145 | filters = None
146 |
147 | if 'filter' in args:
148 | filters = args['filter'][0]
149 |
150 | networks = db.get_networks(ip_addr=filters)
151 |
152 | total = len(networks)
153 |
154 | if total >0:
155 |
156 | for network in networks:
157 |
158 | network_id = network['id']
159 | network['host_count'] = len(db.get_hosts(network_id=network_id))
160 | results['results'].append(network)
161 |
162 | results['total'] = total
163 |
164 | return response.json(results)
165 |
166 |
167 | @doc.summary("Updates networks")
168 | @app.route("/networks/", methods=['POST'])
169 | @protected()
170 | async def update_networks(request):
171 |
172 | results = {}
173 | status_code = 201
174 |
175 | if request.json:
176 | json_query = request.json
177 |
178 | if type(json_query) != dict:
179 | json_query = json.loads(json_query)
180 |
181 | isprivate = is_private(json_query['ip_addr'])
182 | json_query['is_private'] = isprivate
183 |
184 | db.update_network(**json_query)
185 | results = {"status": "created"}
186 |
187 | else:
188 | results = {"status": "error"}
189 | status_code = 500
190 |
191 |
192 |
193 | results = response.json(results, status=status_code)
194 |
195 | return results
196 |
197 |
198 | @doc.summary("Deletes an array of networkIds")
199 | @app.route("/networks/", methods=['DELETE'])
200 | @protected()
201 | async def delete_network_list(request):
202 |
203 | results = {
204 | "status": "deleted"
205 | }
206 |
207 | args = request.args
208 | filters = None
209 |
210 | if request.json:
211 | json_query = request.json
212 | for network_id in json_query:
213 | db.delete_network(network_id=network_id)
214 |
215 | return response.json(results)
216 |
217 |
218 |
219 | @doc.summary("Return all hosts in the selected network_id")
220 | @app.route("/networks//", methods=['GET'])
221 | @protected()
222 | async def get_network_hosts(request, network_id):
223 |
224 | results = {
225 | "network": {
226 | },
227 | "results": []
228 | }
229 |
230 | hosts = db.get_hosts(network_id=network_id)
231 |
232 | for h in hosts:
233 | h = count_ports(h)
234 | results['results'].append(h)
235 |
236 | network = db.get_networks(network_id=network_id)
237 |
238 | results['network'] = network[0]
239 |
240 | results['total'] = len(hosts)
241 |
242 | return response.json(results)
243 |
244 |
245 | @doc.summary("Delete the specified network")
246 | @app.route("/networks//", methods=['DELETE'])
247 | @protected()
248 | async def delete_network(request, network_id):
249 |
250 | results = {}
251 |
252 | db.delete_network(network_id=network_id)
253 | results = {"status": "deleted"}
254 |
255 | return response.json(results, status=200)
256 |
257 |
258 | @doc.summary("Updates hosts in the selected network_id")
259 | @app.route("/networks//", methods=['POST'])
260 | @protected()
261 | async def update_network_hosts(request, network_id):
262 | results = {}
263 |
264 | json_query = request.json
265 |
266 | if type(json_query) != dict:
267 | json_query = json.loads(json_query)
268 | if not 'details' in json_query.keys():
269 | json_query['details'] = {}
270 |
271 | db.new_host(**json_query)
272 | results = {"status": "created"}
273 |
274 | return response.json(results, status=201)
275 |
276 |
277 | """ SENSORS """
278 |
279 |
280 | @doc.summary("Returns all sensors")
281 | @app.route("/sensors/", methods=['GET'])
282 | @protected()
283 | async def get_sensors(request):
284 | sensors = db.get_sensors()
285 | results = {
286 | "total": len(sensors),
287 | "results": sensors
288 | }
289 |
290 | return response.json(results)
291 |
292 |
293 | @doc.summary("Updates sensors")
294 | @app.route("/sensors/", methods=['POST'])
295 | @protected()
296 | async def update_sensors(request):
297 |
298 | results = {}
299 |
300 | json_query = request.json
301 |
302 | if type(json_query) != dict:
303 | json_query = json.loads(json_query)
304 |
305 | db.update_sensor(**json_query)
306 | results = {"status": "created"}
307 |
308 | return response.json(results, status=201)
309 |
310 |
311 | """ CHECK """
312 |
313 |
314 | @doc.summary("Check if api respond")
315 | @app.route("/check", methods=['GET'])
316 | async def check_api(request):
317 | results = {
318 | "status": "success"
319 | }
320 |
321 | return response.json(results)
322 |
323 |
324 | """ METRICS """
325 |
326 |
327 | @doc.summary("Metrics route")
328 | @app.route("/metrics/", methods=['GET'])
329 | @protected()
330 | async def get_metrics(request):
331 | hosts = db.get_hosts()
332 | ports_count = 0
333 |
334 | for host in hosts:
335 | for service in host['services']:
336 | ports_count += 1
337 |
338 | host_count = len(hosts)
339 |
340 | results = {
341 | "host_count": host_count,
342 | "network_count": len(db.get_networks()),
343 | "sensor_count": len(db.get_sensors()),
344 | "ports_count": ports_count
345 | }
346 |
347 | return response.json(results)
348 |
349 |
350 | """ GRAPHS """
351 |
352 |
353 | def last_digit(ip_addr):
354 | result = ip_addr.split('.')[-1]
355 | result = "." + result
356 | return result
357 |
358 | def edge(src, dst):
359 | _edge = {"from": src, "to": dst}
360 | return _edge
361 |
362 |
363 | @doc.summary("Returns graphs v2")
364 | @app.route("/graphs/", methods=['GET'])
365 | @app.route("/graphs//", methods=['GET'])
366 | @protected()
367 | async def get_graphs(request, network_id=None):
368 |
369 | results = {
370 | "nodes": [],
371 | "edges": []
372 | }
373 | filters = {
374 | "network_id": network_id
375 | }
376 |
377 | for network in db.get_networks(**filters):
378 |
379 | gateway = {}
380 |
381 | hosts = db.get_hosts(network_id=network['id'])
382 | if len(hosts) > 0:
383 |
384 | for host in hosts:
385 | if is_gateway(host):
386 | gateway = host
387 |
388 | # Make host label
389 | label = "{}\n {}".format(
390 | last_digit(host['ip_addr']),
391 | host['dns_names']
392 | )
393 |
394 | # Make host id + network_id etc..
395 | node = {"id": host['id'], "label": label, "group": host['network_id']}
396 | results["nodes"].append(node)
397 |
398 | if gateway != {}:
399 | for host in hosts:
400 | if host != gateway:
401 | if host['network_id'] == gateway['network_id']:
402 | host_to_gw = edge(host['id'], gateway['id'])
403 | results['edges'].append(host_to_gw)
404 |
405 |
406 | return response.json(results)
407 |
408 |
409 | """ USERS """
410 |
411 |
412 | @doc.summary("Get all users")
413 | @app.route("/users/", methods=['GET'])
414 | @protected()
415 | async def get_users(request):
416 |
417 | results = {
418 | "results": []
419 | }
420 | users = db.get_users()
421 | for u in users:
422 | u.pop('password', None)
423 |
424 | results['results'] = users
425 | return response.json(results, status=200)
426 |
427 |
428 |
429 | """ MAC """
430 |
431 |
432 | @doc.summary("Resolve mac address's vendor")
433 | @app.route("/macvendor//", methods=['GET'])
434 | @protected()
435 | async def get_macvendor(request, mac_addr):
436 |
437 | results = {
438 | "mac_addr": mac_addr,
439 | "vendor": mactools.get_mac_vendor(mac_addr)
440 | }
441 |
442 | return response.json(results, status=200)
443 |
444 |
445 | """ SUGGESTIONS/SEARCH """
446 |
447 |
448 | @doc.summary("Search endpoint")
449 | @app.route("/search", methods=['GET'])
450 | @protected()
451 | async def search(request):
452 | query = {}
453 | text_query = request.args.get('q')
454 |
455 | if text_query:
456 | query['text_query'] = text_query
457 |
458 | hosts = db.get_hosts(**query)
459 |
460 | results = {
461 | "total": len(hosts),
462 | "results": hosts
463 | }
464 |
465 | return response.json(results)
466 |
467 |
468 | """ PLUGINS """
469 |
470 | plugin_list = list_plugins(fullpath + '/data/plugins')
471 |
472 | @doc.summary("List all available plugins and versions")
473 | @app.route("/plugins/", methods=['GET'])
474 | @protected()
475 | async def get_plugins(request):
476 | query = {}
477 |
478 |
479 | results = {
480 | "total": len(plugin_list),
481 | "results": plugin_list
482 | }
483 |
484 | return response.json(results)
485 |
486 |
487 | if __name__ == "__main__":
488 |
489 |
490 | app.config.LOGO = False
491 | app.config.SANIC_JWT_USER_ID = 'id'
492 | app.config.SANIC_JWT_SECRET = 'gkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI9Gl6VRM+2s8CAg'
493 | app.config.SANIC_JWT_EXPIRATION_DELTA = 28800
494 | # app.config.SANIC_JWT_EXPIRATION_DELTA = 30
495 |
496 | logging.getLogger('sanic_cors').level = logging.DEBUG
497 |
498 | write("Starting API...", 3)
499 |
500 | if production:
501 | write("Production mode enabled", 0)
502 | else:
503 | write("Production mode disabled", 1)
504 |
505 | while db.server_status() != True:
506 | write("Waiting for the database to be UP...", 3)
507 | sleep(1)
508 | db.add_user(email='admin', password='password')
509 | write("Added default user admin/password to database", 0)
510 |
511 | app.run(host="0.0.0.0", port=8080, debug=debug)
512 |
--------------------------------------------------------------------------------