forked from p15670423/monkey
PBA config depth reduced to 3, unused methods removed, pba file size
property removed, small refactors and comments.
This commit is contained in:
parent
dbf56b5531
commit
92615b848d
|
@ -271,12 +271,10 @@ class Configuration(object):
|
||||||
extract_azure_creds = True
|
extract_azure_creds = True
|
||||||
|
|
||||||
post_breach_actions = []
|
post_breach_actions = []
|
||||||
custom_post_breach = {"linux": "",
|
custom_pba_linux_cmd = ""
|
||||||
"windows": "",
|
custom_pba_windows_cmd = ""
|
||||||
"linux_file": None,
|
custom_pba_linux_file_info = None
|
||||||
"windows_file": None,
|
custom_pba_windows_file_info = None
|
||||||
"linux_file_info": None,
|
|
||||||
"windows_file_info": None}
|
|
||||||
|
|
||||||
|
|
||||||
WormConfiguration = Configuration()
|
WormConfiguration = Configuration()
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from infection_monkey.post_breach.pba import PBA
|
from infection_monkey.post_breach.pba import PBA
|
||||||
from infection_monkey.control import ControlClient
|
from infection_monkey.control import ControlClient
|
||||||
import infection_monkey.monkeyfs as monkeyfs
|
|
||||||
from infection_monkey.config import WormConfiguration
|
from infection_monkey.config import WormConfiguration
|
||||||
import requests
|
import requests
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -11,84 +10,65 @@ LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
__author__ = 'VakarisZ'
|
__author__ = 'VakarisZ'
|
||||||
|
|
||||||
DOWNLOAD_CHUNK = 1024
|
# Default commands for executing PBA file and then removing it
|
||||||
DEFAULT_LINUX_COMMAND = "chmod +x {0} ; {0} ; rm {0}"
|
DEFAULT_LINUX_COMMAND = "chmod +x {0} ; {0} ; rm {0}"
|
||||||
DEFAULT_WINDOWS_COMMAND = "{0} & del {0}"
|
DEFAULT_WINDOWS_COMMAND = "{0} & del {0}"
|
||||||
|
|
||||||
|
|
||||||
class FileExecution(PBA):
|
class FileExecution(PBA):
|
||||||
|
"""
|
||||||
|
Defines user's file execution post breach action.
|
||||||
|
"""
|
||||||
def __init__(self, linux_command="", windows_command=""):
|
def __init__(self, linux_command="", windows_command=""):
|
||||||
self.linux_file_info = WormConfiguration.custom_post_breach['linux_file_info']
|
self.linux_filename = WormConfiguration.PBA_linux_filename
|
||||||
self.windows_file_info = WormConfiguration.custom_post_breach['windows_file_info']
|
self.windows_filename = WormConfiguration.PBA_windows_filename
|
||||||
super(FileExecution, self).__init__("File execution", linux_command, windows_command)
|
super(FileExecution, self).__init__("File execution", linux_command, windows_command)
|
||||||
|
|
||||||
def execute_linux(self):
|
def execute_linux(self):
|
||||||
FileExecution.download_PBA_file(FileExecution.get_dest_dir(WormConfiguration, True),
|
FileExecution.download_PBA_file(FileExecution.get_dest_dir(WormConfiguration, True),
|
||||||
self.linux_file_info['name'],
|
self.linux_filename)
|
||||||
self.linux_file_info['size'])
|
|
||||||
return super(FileExecution, self).execute_linux()
|
return super(FileExecution, self).execute_linux()
|
||||||
|
|
||||||
def execute_win(self):
|
def execute_win(self):
|
||||||
FileExecution.download_PBA_file(FileExecution.get_dest_dir(WormConfiguration, True),
|
FileExecution.download_PBA_file(FileExecution.get_dest_dir(WormConfiguration, True),
|
||||||
self.windows_file_info['name'],
|
self.windows_filename)
|
||||||
self.windows_file_info['size'])
|
|
||||||
return super(FileExecution, self).execute_win()
|
return super(FileExecution, self).execute_win()
|
||||||
|
|
||||||
def add_default_command(self, is_linux):
|
def add_default_command(self, is_linux):
|
||||||
|
"""
|
||||||
|
Replaces current (likely empty) command with default file execution command.
|
||||||
|
:param is_linux: Boolean that indicates for which OS the command is being set.
|
||||||
|
"""
|
||||||
if is_linux:
|
if is_linux:
|
||||||
file_path = os.path.join(FileExecution.get_dest_dir(WormConfiguration, is_linux=True),
|
file_path = os.path.join(FileExecution.get_dest_dir(WormConfiguration, is_linux=True),
|
||||||
self.linux_file_info["name"])
|
self.linux_filename)
|
||||||
self.linux_command = DEFAULT_LINUX_COMMAND.format(file_path)
|
self.linux_command = DEFAULT_LINUX_COMMAND.format(file_path)
|
||||||
else:
|
else:
|
||||||
file_path = os.path.join(FileExecution.get_dest_dir(WormConfiguration, is_linux=False),
|
file_path = os.path.join(FileExecution.get_dest_dir(WormConfiguration, is_linux=False),
|
||||||
self.windows_file_info["name"])
|
self.windows_filename)
|
||||||
self.windows_command = DEFAULT_WINDOWS_COMMAND.format(file_path)
|
self.windows_command = DEFAULT_WINDOWS_COMMAND.format(file_path)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def download_PBA_file(dst_dir, filename, size):
|
def download_PBA_file(dst_dir, filename):
|
||||||
"""
|
"""
|
||||||
Handles post breach action file download
|
Handles post breach action file download
|
||||||
:param dst_dir: Destination directory
|
:param dst_dir: Destination directory
|
||||||
:param filename: Filename
|
:param filename: Filename
|
||||||
:param size: File size in bytes
|
|
||||||
:return: True if successful, false otherwise
|
:return: True if successful, false otherwise
|
||||||
"""
|
"""
|
||||||
PBA_file_v_path = FileExecution.download_PBA_file_to_vfs(filename, size)
|
|
||||||
|
PBA_file_contents = requests.get("https://%s/api/pba/download/%s" %
|
||||||
|
(WormConfiguration.current_server, filename),
|
||||||
|
verify=False,
|
||||||
|
proxies=ControlClient.proxies)
|
||||||
try:
|
try:
|
||||||
with monkeyfs.open(PBA_file_v_path, "rb") as downloaded_PBA_file:
|
|
||||||
with open(os.path.join(dst_dir, filename), 'wb') as written_PBA_file:
|
with open(os.path.join(dst_dir, filename), 'wb') as written_PBA_file:
|
||||||
shutil.copyfileobj(downloaded_PBA_file, written_PBA_file)
|
shutil.copyfileobj(PBA_file_contents, written_PBA_file)
|
||||||
return True
|
return True
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
LOG.error("Can not download post breach file to target machine, because %s" % e)
|
LOG.error("Can not download post breach file to target machine, because %s" % e)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def download_PBA_file_to_vfs(filename, size):
|
|
||||||
if not WormConfiguration.current_server:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
dest_file = monkeyfs.virtual_path(filename)
|
|
||||||
if (monkeyfs.isfile(dest_file)) and (size == monkeyfs.getsize(dest_file)):
|
|
||||||
return dest_file
|
|
||||||
else:
|
|
||||||
download = requests.get("https://%s/api/pba/download/%s" %
|
|
||||||
(WormConfiguration.current_server, filename),
|
|
||||||
verify=False,
|
|
||||||
proxies=ControlClient.proxies)
|
|
||||||
|
|
||||||
with monkeyfs.open(dest_file, 'wb') as file_obj:
|
|
||||||
for chunk in download.iter_content(chunk_size=DOWNLOAD_CHUNK):
|
|
||||||
if chunk:
|
|
||||||
file_obj.write(chunk)
|
|
||||||
file_obj.flush()
|
|
||||||
if size == monkeyfs.getsize(dest_file):
|
|
||||||
return dest_file
|
|
||||||
|
|
||||||
except Exception as exc:
|
|
||||||
LOG.warn("Error connecting to control server %s: %s",
|
|
||||||
WormConfiguration.current_server, exc)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_dest_dir(config, is_linux):
|
def get_dest_dir(config, is_linux):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -5,21 +5,34 @@ import socket
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
__author__ = 'VakarisZ'
|
||||||
|
|
||||||
|
|
||||||
# Post Breach Action container
|
|
||||||
class PBA(object):
|
class PBA(object):
|
||||||
|
"""
|
||||||
|
Post breach action object. Can be extended to support more than command execution on target machine.
|
||||||
|
"""
|
||||||
def __init__(self, name="unknown", linux_command="", windows_command=""):
|
def __init__(self, name="unknown", linux_command="", windows_command=""):
|
||||||
|
"""
|
||||||
|
:param name: Name of post breach action.
|
||||||
|
:param linux_command: Command that will be executed on linux machine
|
||||||
|
:param windows_command: Command that will be executed on windows machine
|
||||||
|
"""
|
||||||
self.linux_command = linux_command
|
self.linux_command = linux_command
|
||||||
self.windows_command = windows_command
|
self.windows_command = windows_command
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
def run(self, is_linux):
|
def run(self, is_linux):
|
||||||
|
"""
|
||||||
|
Runs post breach action command
|
||||||
|
:param is_linux: boolean that indicates on which os monkey is running
|
||||||
|
"""
|
||||||
if is_linux:
|
if is_linux:
|
||||||
command = self.linux_command
|
command = self.linux_command
|
||||||
exec_funct = self.execute_linux
|
exec_funct = self._execute_linux
|
||||||
else:
|
else:
|
||||||
command = self.windows_command
|
command = self.windows_command
|
||||||
exec_funct = self.execute_win
|
exec_funct = self._execute_win
|
||||||
if command:
|
if command:
|
||||||
hostname = socket.gethostname()
|
hostname = socket.gethostname()
|
||||||
ControlClient.send_telemetry('post_breach', {'command': command,
|
ControlClient.send_telemetry('post_breach', {'command': command,
|
||||||
|
@ -29,20 +42,22 @@ class PBA(object):
|
||||||
'ip': socket.gethostbyname(hostname)
|
'ip': socket.gethostbyname(hostname)
|
||||||
})
|
})
|
||||||
|
|
||||||
def execute_linux(self):
|
def _execute_linux(self):
|
||||||
# Default linux PBA execution function. Override if additional functionality is needed
|
"""
|
||||||
if self.linux_command:
|
Default linux PBA execution function. Override it if additional functionality is needed
|
||||||
try:
|
"""
|
||||||
return subprocess.check_output(self.linux_command, stderr=subprocess.STDOUT, shell=True)
|
self._execute_default(self.linux_command)
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
# Return error output of the command
|
|
||||||
return e.output
|
|
||||||
|
|
||||||
def execute_win(self):
|
def _execute_win(self):
|
||||||
# Default windows PBA execution function. Override if additional functionality is needed
|
"""
|
||||||
if self.windows_command:
|
Default linux PBA execution function. Override it if additional functionality is needed
|
||||||
|
"""
|
||||||
|
self._execute_default(self.windows_command)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _execute_default(command):
|
||||||
try:
|
try:
|
||||||
return subprocess.check_output(self.windows_command, stderr=subprocess.STDOUT, shell=True)
|
return subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
# Return error output of the command
|
# Return error output of the command
|
||||||
return e.output
|
return e.output
|
||||||
|
|
|
@ -3,19 +3,25 @@ import infection_monkey.config
|
||||||
import platform
|
import platform
|
||||||
from file_execution import FileExecution
|
from file_execution import FileExecution
|
||||||
from pba import PBA
|
from pba import PBA
|
||||||
|
from infection_monkey.utils import is_windows_os
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
__author__ = 'VakarisZ'
|
__author__ = 'VakarisZ'
|
||||||
|
|
||||||
|
|
||||||
# Class that handles post breach action execution
|
|
||||||
class PostBreach(object):
|
class PostBreach(object):
|
||||||
|
"""
|
||||||
|
This class handles post breach actions execution
|
||||||
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.os_is_linux = False if platform.system() == 'Windows' else True
|
self.os_is_linux = not is_windows_os()
|
||||||
self.pba_list = self.config_to_pba_list(infection_monkey.config.WormConfiguration)
|
self.pba_list = self.config_to_pba_list(infection_monkey.config.WormConfiguration)
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
|
"""
|
||||||
|
Executes all post breach actions.
|
||||||
|
"""
|
||||||
for pba in self.pba_list:
|
for pba in self.pba_list:
|
||||||
pba.run(self.os_is_linux)
|
pba.run(self.os_is_linux)
|
||||||
LOG.info("Post breach actions executed")
|
LOG.info("Post breach actions executed")
|
||||||
|
@ -24,40 +30,45 @@ class PostBreach(object):
|
||||||
def config_to_pba_list(config):
|
def config_to_pba_list(config):
|
||||||
"""
|
"""
|
||||||
Returns a list of PBA objects generated from config.
|
Returns a list of PBA objects generated from config.
|
||||||
|
:param config: Monkey configuration
|
||||||
|
:return: A list of PBA objects.
|
||||||
|
TODO: Parse PBA's from PBA array (like 'add_user'). Also merge the whole outdated PBA structure into this one.
|
||||||
"""
|
"""
|
||||||
pba_list = []
|
pba_list = []
|
||||||
pba_list.extend(PostBreach.get_custom(config))
|
pba_list.extend(PostBreach.get_custom_PBA(config))
|
||||||
|
|
||||||
return pba_list
|
return pba_list
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_custom(config):
|
def get_custom_PBA(config):
|
||||||
|
"""
|
||||||
|
Creates post breach actions depending on users input into 'custom post breach' config section
|
||||||
|
:param config: monkey's configuration
|
||||||
|
:return: List of PBA objects ([user's file execution PBA, user's command execution PBA])
|
||||||
|
"""
|
||||||
custom_list = []
|
custom_list = []
|
||||||
file_pba = FileExecution()
|
file_pba = FileExecution()
|
||||||
command_pba = PBA(name="Custom")
|
command_pba = PBA(name="Custom")
|
||||||
post_breach = config.custom_post_breach
|
|
||||||
linux_command = post_breach['linux']
|
|
||||||
windows_command = post_breach['windows']
|
|
||||||
|
|
||||||
# Add commands to linux pba
|
# Add linux commands to PBA's
|
||||||
if post_breach['linux_file_info']['name']:
|
if config['PBA_linux_filename']:
|
||||||
if linux_command:
|
if config['custom_PBA_linux_cmd']:
|
||||||
file_pba.linux_command=linux_command
|
file_pba.linux_command = config['custom_PBA_linux_cmd']
|
||||||
else:
|
else:
|
||||||
file_pba.add_default_command(is_linux=True)
|
file_pba.add_default_command(is_linux=True)
|
||||||
elif linux_command:
|
elif config['custom_PBA_linux_cmd']:
|
||||||
command_pba.linux_command = linux_command
|
command_pba.linux_command = config['custom_PBA_linux_cmd']
|
||||||
|
|
||||||
# Add commands to windows pba
|
# Add windows commands to PBA's
|
||||||
if post_breach['windows_file_info']['name']:
|
if config['PBA_windows_filename']:
|
||||||
if windows_command:
|
if config['custom_PBA_windows_cmd']:
|
||||||
file_pba.windows_command=windows_command
|
file_pba.windows_command = config['custom_PBA_windows_cmd']
|
||||||
else:
|
else:
|
||||||
file_pba.add_default_command(is_linux=False)
|
file_pba.add_default_command(is_linux=False)
|
||||||
elif windows_command:
|
elif config['custom_PBA_windows_cmd']:
|
||||||
command_pba.windows_command = windows_command
|
command_pba.windows_command = config['custom_PBA_windows_cmd']
|
||||||
|
|
||||||
# Add pba's to list
|
# Add PBA's to list
|
||||||
if file_pba.linux_command or file_pba.windows_command:
|
if file_pba.linux_command or file_pba.windows_command:
|
||||||
custom_list.append(file_pba)
|
custom_list.append(file_pba)
|
||||||
if command_pba.windows_command or command_pba.linux_command:
|
if command_pba.windows_command or command_pba.linux_command:
|
||||||
|
|
|
@ -25,9 +25,6 @@ class Monkey(flask_restful.Resource):
|
||||||
if guid:
|
if guid:
|
||||||
monkey_json = mongo.db.monkey.find_one_or_404({"guid": guid})
|
monkey_json = mongo.db.monkey.find_one_or_404({"guid": guid})
|
||||||
monkey_json['config'] = ConfigService.decrypt_flat_config(monkey_json['config'])
|
monkey_json['config'] = ConfigService.decrypt_flat_config(monkey_json['config'])
|
||||||
# Don't send file contents to the monkey
|
|
||||||
monkey_json['config']['custom_post_breach']['linux_file'] = ''
|
|
||||||
monkey_json['config']['custom_post_breach']['windows_file'] = ''
|
|
||||||
return monkey_json
|
return monkey_json
|
||||||
|
|
||||||
return {}
|
return {}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
|
|
||||||
import flask_restful
|
import flask_restful
|
||||||
from flask import request, send_from_directory
|
from flask import send_from_directory
|
||||||
|
from cc.services.config import UPLOADS_DIR
|
||||||
|
|
||||||
UPLOADS_DIR = "./userUploads"
|
__author__ = 'VakarisZ'
|
||||||
|
|
||||||
|
|
||||||
class PBAFileDownload(flask_restful.Resource):
|
class PBAFileDownload(flask_restful.Resource):
|
||||||
|
"""
|
||||||
|
File download endpoint used by monkey to download user's PBA file
|
||||||
|
"""
|
||||||
# Used by monkey. can't secure.
|
# Used by monkey. can't secure.
|
||||||
def get(self, path):
|
def get(self, path):
|
||||||
return send_from_directory(UPLOADS_DIR, path)
|
return send_from_directory(UPLOADS_DIR, path)
|
||||||
|
|
|
@ -1,30 +1,46 @@
|
||||||
import flask_restful
|
import flask_restful
|
||||||
from flask import request, send_from_directory, Response
|
from flask import request, send_from_directory, Response
|
||||||
from cc.services.config import ConfigService, WINDOWS_PBA_INFO, LINUX_PBA_INFO
|
from cc.services.config import ConfigService, PBA_WINDOWS_FILENAME_PATH, PBA_LINUX_FILENAME_PATH, UPLOADS_DIR
|
||||||
|
from cc.auth import jwt_required
|
||||||
import os
|
import os
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
import logging
|
import logging
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
|
__author__ = 'VakarisZ'
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
UPLOADS_DIR = "./monkey_island/cc/userUploads"
|
|
||||||
GET_FILE_DIR = "./userUploads"
|
GET_FILE_DIR = "./userUploads"
|
||||||
# What endpoints front end uses to identify which files to work with
|
# Front end uses these strings to identify which files to work with (linux of windows)
|
||||||
LINUX_PBA_TYPE = 'PBAlinux'
|
LINUX_PBA_TYPE = 'PBAlinux'
|
||||||
WINDOWS_PBA_TYPE = 'PBAwindows'
|
WINDOWS_PBA_TYPE = 'PBAwindows'
|
||||||
|
|
||||||
|
|
||||||
class FileUpload(flask_restful.Resource):
|
class FileUpload(flask_restful.Resource):
|
||||||
|
"""
|
||||||
|
File upload endpoint used to exchange files with filepond component on the front-end
|
||||||
|
"""
|
||||||
|
@jwt_required()
|
||||||
def get(self, file_type):
|
def get(self, file_type):
|
||||||
|
"""
|
||||||
|
Sends file to filepond
|
||||||
|
:param file_type: Type indicates which file to send, linux or windows
|
||||||
|
:return: Returns file contents
|
||||||
|
"""
|
||||||
# Verify that file_name is indeed a file from config
|
# Verify that file_name is indeed a file from config
|
||||||
if file_type == LINUX_PBA_TYPE:
|
if file_type == LINUX_PBA_TYPE:
|
||||||
filename = ConfigService.get_config_value(copy.deepcopy(LINUX_PBA_INFO))['name']
|
filename = ConfigService.get_config_value(copy.deepcopy(PBA_LINUX_FILENAME_PATH))
|
||||||
else:
|
else:
|
||||||
filename = ConfigService.get_config_value(copy.deepcopy(WINDOWS_PBA_INFO))['name']
|
filename = ConfigService.get_config_value(copy.deepcopy(PBA_WINDOWS_FILENAME_PATH))
|
||||||
return send_from_directory(GET_FILE_DIR, filename)
|
return send_from_directory(GET_FILE_DIR, filename)
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
def post(self, file_type):
|
def post(self, file_type):
|
||||||
|
"""
|
||||||
|
Receives user's uploaded file from filepond
|
||||||
|
:param file_type: Type indicates which file was received, linux or windows
|
||||||
|
:return: Returns flask response object with uploaded file's filename
|
||||||
|
"""
|
||||||
filename = FileUpload.upload_pba_file(request, (file_type == LINUX_PBA_TYPE))
|
filename = FileUpload.upload_pba_file(request, (file_type == LINUX_PBA_TYPE))
|
||||||
|
|
||||||
response = Response(
|
response = Response(
|
||||||
|
@ -32,9 +48,15 @@ class FileUpload(flask_restful.Resource):
|
||||||
status=200, mimetype='text/plain')
|
status=200, mimetype='text/plain')
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@jwt_required()
|
||||||
def delete(self, file_type):
|
def delete(self, file_type):
|
||||||
file_conf_path = LINUX_PBA_INFO if file_type == 'PBAlinux' else WINDOWS_PBA_INFO
|
"""
|
||||||
filename = ConfigService.get_config_value(file_conf_path)['name']
|
Deletes file that has been deleted on the front end
|
||||||
|
:param file_type: Type indicates which file was deleted, linux of windows
|
||||||
|
:return: Empty response
|
||||||
|
"""
|
||||||
|
file_conf_path = PBA_LINUX_FILENAME_PATH if file_type == 'PBAlinux' else PBA_WINDOWS_FILENAME_PATH
|
||||||
|
filename = ConfigService.get_config_value(file_conf_path)
|
||||||
file_path = os.path.join(UPLOADS_DIR, filename)
|
file_path = os.path.join(UPLOADS_DIR, filename)
|
||||||
try:
|
try:
|
||||||
if os.path.exists(file_path):
|
if os.path.exists(file_path):
|
||||||
|
@ -47,26 +69,14 @@ class FileUpload(flask_restful.Resource):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def upload_pba_file(request_, is_linux=True):
|
def upload_pba_file(request_, is_linux=True):
|
||||||
|
"""
|
||||||
|
Uploads PBA file to island's file system
|
||||||
|
:param request_: Request object containing PBA file
|
||||||
|
:param is_linux: Boolean indicating if this file is for windows or for linux
|
||||||
|
:return: filename string
|
||||||
|
"""
|
||||||
filename = secure_filename(request_.files['filepond'].filename)
|
filename = secure_filename(request_.files['filepond'].filename)
|
||||||
file_path = os.path.join(UPLOADS_DIR, filename)
|
file_path = os.path.join(UPLOADS_DIR, filename)
|
||||||
request_.files['filepond'].save(file_path)
|
request_.files['filepond'].save(file_path)
|
||||||
file_size = os.path.getsize(file_path)
|
ConfigService.set_config_value((PBA_LINUX_FILENAME_PATH if is_linux else PBA_WINDOWS_FILENAME_PATH), filename)
|
||||||
ConfigService.set_config_value((LINUX_PBA_INFO if is_linux else WINDOWS_PBA_INFO),
|
|
||||||
{'size': file_size, 'name': filename})
|
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_file_info_db_paths(is_linux=True):
|
|
||||||
"""
|
|
||||||
Gets PBA file size and name parameter config paths for linux and windows
|
|
||||||
:param is_linux: determines whether to get windows or linux file params
|
|
||||||
:return: returns tuple of filename and file size paths in config
|
|
||||||
"""
|
|
||||||
if is_linux:
|
|
||||||
file_info = 'linux_file_info'
|
|
||||||
else:
|
|
||||||
file_info = 'windows_file_info'
|
|
||||||
config_path = 'monkey.behaviour.custom_post_breach.' + file_info + '.'
|
|
||||||
size_path = config_path + 'size'
|
|
||||||
name_path = config_path + 'name'
|
|
||||||
return name_path, size_path
|
|
||||||
|
|
|
@ -36,14 +36,11 @@ ENCRYPTED_CONFIG_STRINGS = \
|
||||||
['cnc', 'aws_config', 'aws_secret_access_key']
|
['cnc', 'aws_config', 'aws_secret_access_key']
|
||||||
]
|
]
|
||||||
|
|
||||||
UPLOADS_DIR = './monkey_island/cc/userUploads'
|
UPLOADS_DIR = '/cc/userUploads'
|
||||||
|
|
||||||
# Where to find file info in config
|
# Where to find file names in config
|
||||||
PBA_CONF_PATH = ['monkey', 'behaviour', 'custom_post_breach']
|
PBA_WINDOWS_FILENAME_PATH = ['monkey', 'behaviour', 'PBA_windows_filename']
|
||||||
WINDOWS_PBA_INFO = copy.deepcopy(PBA_CONF_PATH)
|
PBA_LINUX_FILENAME_PATH = ['monkey', 'behaviour', 'PBA_linux_filename']
|
||||||
WINDOWS_PBA_INFO.append('windows_file_info')
|
|
||||||
LINUX_PBA_INFO = copy.deepcopy(PBA_CONF_PATH)
|
|
||||||
LINUX_PBA_INFO.append('linux_file_info')
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigService:
|
class ConfigService:
|
||||||
|
@ -78,13 +75,11 @@ class ConfigService:
|
||||||
:param is_initial_config: If True, returns the value of the initial config instead of the current config.
|
:param is_initial_config: If True, returns the value of the initial config instead of the current config.
|
||||||
:param should_decrypt: If True, the value of the config key will be decrypted
|
:param should_decrypt: If True, the value of the config key will be decrypted
|
||||||
(if it's in the list of encrypted config values).
|
(if it's in the list of encrypted config values).
|
||||||
:return: The value of the requested config key or False, if such key doesn't exist.
|
:return: The value of the requested config key.
|
||||||
"""
|
"""
|
||||||
config_key = functools.reduce(lambda x, y: x + '.' + y, config_key_as_arr)
|
config_key = functools.reduce(lambda x, y: x + '.' + y, config_key_as_arr)
|
||||||
config = mongo.db.config.find_one({'name': 'initial' if is_initial_config else 'newconfig'}, {config_key: 1})
|
config = mongo.db.config.find_one({'name': 'initial' if is_initial_config else 'newconfig'}, {config_key: 1})
|
||||||
for config_key_part in config_key_as_arr:
|
for config_key_part in config_key_as_arr:
|
||||||
if config_key_part not in config:
|
|
||||||
return False
|
|
||||||
config = config[config_key_part]
|
config = config[config_key_part]
|
||||||
if should_decrypt:
|
if should_decrypt:
|
||||||
if config_key_as_arr in ENCRYPTED_CONFIG_ARRAYS:
|
if config_key_as_arr in ENCRYPTED_CONFIG_ARRAYS:
|
||||||
|
@ -158,7 +153,7 @@ class ConfigService:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_config(config_json, should_encrypt):
|
def update_config(config_json, should_encrypt):
|
||||||
# Island file upload happens on pba_file_upload endpoint and config is set there
|
# PBA file upload happens on pba_file_upload endpoint and corresponding config options are set there
|
||||||
ConfigService.keep_PBA_files(config_json)
|
ConfigService.keep_PBA_files(config_json)
|
||||||
if should_encrypt:
|
if should_encrypt:
|
||||||
try:
|
try:
|
||||||
|
@ -173,14 +168,14 @@ class ConfigService:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def keep_PBA_files(config_json):
|
def keep_PBA_files(config_json):
|
||||||
"""
|
"""
|
||||||
file_upload endpoint handles file upload and sets config asynchronously.
|
Sets PBA file info in config_json to current config's PBA file info values.
|
||||||
This saves file info from being overridden.
|
:param config_json: config_json that will be modified
|
||||||
"""
|
"""
|
||||||
if ConfigService.get_config():
|
if ConfigService.get_config():
|
||||||
linux_info = ConfigService.get_config_value(LINUX_PBA_INFO)
|
linux_filename = ConfigService.get_config_value(PBA_LINUX_FILENAME_PATH)
|
||||||
windows_info = ConfigService.get_config_value(WINDOWS_PBA_INFO)
|
windows_filename = ConfigService.get_config_value(PBA_WINDOWS_FILENAME_PATH)
|
||||||
config_json['monkey']['behaviour']['custom_post_breach']['linux_file_info'] = linux_info
|
config_json['monkey']['behaviour']['PBA_linux_filename'] = linux_filename
|
||||||
config_json['monkey']['behaviour']['custom_post_breach']['windows_file_info'] = windows_info
|
config_json['monkey']['behaviour']['PBA_windows_filename'] = windows_filename
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def init_default_config():
|
def init_default_config():
|
||||||
|
@ -239,7 +234,13 @@ class ConfigService:
|
||||||
if instance != {}:
|
if instance != {}:
|
||||||
return
|
return
|
||||||
for property, subschema in properties.iteritems():
|
for property, subschema in properties.iteritems():
|
||||||
main_dict = ConfigService.r_get_properties(subschema)
|
main_dict = {}
|
||||||
|
for property2, subschema2 in subschema["properties"].iteritems():
|
||||||
|
sub_dict = {}
|
||||||
|
for property3, subschema3 in subschema2["properties"].iteritems():
|
||||||
|
if "default" in subschema3:
|
||||||
|
sub_dict[property3] = subschema3["default"]
|
||||||
|
main_dict[property2] = sub_dict
|
||||||
instance.setdefault(property, main_dict)
|
instance.setdefault(property, main_dict)
|
||||||
|
|
||||||
for error in validate_properties(validator, properties, instance, schema):
|
for error in validate_properties(validator, properties, instance, schema):
|
||||||
|
@ -249,17 +250,6 @@ class ConfigService:
|
||||||
validator_class, {"properties": set_defaults},
|
validator_class, {"properties": set_defaults},
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def r_get_properties(schema):
|
|
||||||
""" Recursively gets all nested properties in schema"""
|
|
||||||
if "default" in schema:
|
|
||||||
return schema["default"]
|
|
||||||
if "properties" in schema:
|
|
||||||
dict_ = {}
|
|
||||||
for property, subschema in schema["properties"].iteritems():
|
|
||||||
dict_[property] = ConfigService.r_get_properties(subschema)
|
|
||||||
return dict_
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def decrypt_config(config):
|
def decrypt_config(config):
|
||||||
ConfigService._encrypt_or_decrypt_config(config, True)
|
ConfigService._encrypt_or_decrypt_config(config, True)
|
||||||
|
@ -322,27 +312,6 @@ class ConfigService:
|
||||||
pair['private_key'] = encryptor.dec(pair['private_key'])
|
pair['private_key'] = encryptor.dec(pair['private_key'])
|
||||||
return pair
|
return pair
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def add_PBA_files(post_breach_files):
|
|
||||||
"""
|
|
||||||
Interceptor of config saving process that uploads PBA files to server
|
|
||||||
and saves filenames and sizes in config instead of full file.
|
|
||||||
:param post_breach_files: Data URL encoded files
|
|
||||||
"""
|
|
||||||
# If any file is uploaded
|
|
||||||
if any(file_name in post_breach_files for file_name in ['linux_file', 'windows_file']):
|
|
||||||
# Create directory for file uploads if not present
|
|
||||||
if not os.path.exists(UPLOADS_DIR):
|
|
||||||
os.makedirs(UPLOADS_DIR)
|
|
||||||
if 'linux_file' in post_breach_files:
|
|
||||||
linux_name, linux_size = ConfigService.upload_file(post_breach_files['linux_file'], UPLOADS_DIR)
|
|
||||||
post_breach_files['linux_file_info']['name'] = linux_name
|
|
||||||
post_breach_files['linux_file_info']['size'] = linux_size
|
|
||||||
if 'windows_file' in post_breach_files:
|
|
||||||
windows_name, windows_size = ConfigService.upload_file(post_breach_files['windows_file'], UPLOADS_DIR)
|
|
||||||
post_breach_files['windows_file_info']['name'] = windows_name
|
|
||||||
post_breach_files['windows_file_info']['size'] = windows_size
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def remove_PBA_files():
|
def remove_PBA_files():
|
||||||
if ConfigService.get_config():
|
if ConfigService.get_config():
|
||||||
|
@ -363,24 +332,3 @@ class ConfigService:
|
||||||
os.remove(file_path)
|
os.remove(file_path)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
logger.error("Can't remove previously uploaded post breach files: %s" % e)
|
logger.error("Can't remove previously uploaded post breach files: %s" % e)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def upload_file(file_data, directory):
|
|
||||||
"""
|
|
||||||
We parse data URL format of the file and save it
|
|
||||||
:param file_data: file encoded in data URL format
|
|
||||||
:param directory: where to save the file
|
|
||||||
:return: filename of saved file
|
|
||||||
"""
|
|
||||||
file_parts = file_data.split(';')
|
|
||||||
if len(file_parts) != 3 and not file_parts[2].startswith('base64,'):
|
|
||||||
logger.error("Invalid file format was submitted to the server")
|
|
||||||
return False
|
|
||||||
# Strip 'name=' from this field and secure the filename
|
|
||||||
filename = secure_filename(file_parts[1][5:])
|
|
||||||
file_path = os.path.join(directory, filename)
|
|
||||||
with open(file_path, 'wb') as file_:
|
|
||||||
file_.write(base64.decodestring(file_parts[2][7:]))
|
|
||||||
file_size = os.path.getsize(file_path)
|
|
||||||
return [filename, file_size]
|
|
||||||
|
|
|
@ -304,7 +304,6 @@ SCHEMA = {
|
||||||
"$ref": "#/definitions/post_breach_acts"
|
"$ref": "#/definitions/post_breach_acts"
|
||||||
},
|
},
|
||||||
"default": [
|
"default": [
|
||||||
"BackdoorUser",
|
|
||||||
],
|
],
|
||||||
"description": "List of actions the Monkey will run post breach"
|
"description": "List of actions the Monkey will run post breach"
|
||||||
},
|
},
|
||||||
|
@ -314,69 +313,46 @@ SCHEMA = {
|
||||||
"title": "Behaviour",
|
"title": "Behaviour",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"custom_post_breach": {
|
"custom_PBA_linux_cmd": {
|
||||||
"title": "Custom post breach actions",
|
"title": "Linux post breach command",
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"linux": {
|
|
||||||
"title": "Linux command",
|
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
"description": "Linux command to execute after breaching."
|
"description": "Linux command to be executed after breaching."
|
||||||
},
|
},
|
||||||
"linux_file": {
|
"PBA_linux_file": {
|
||||||
"title": "Linux file",
|
"title": "Linux post breach file",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "data-url",
|
"format": "data-url",
|
||||||
"description": "File to be executed after breaching. "
|
"description": "File to be executed after breaching. "
|
||||||
"If you want custom execution behavior, "
|
"If you want custom execution behavior, "
|
||||||
"specify it in 'Linux command' field. "
|
"specify it in 'Linux post breach command' field. "
|
||||||
|
"Reference your file by filename."
|
||||||
},
|
},
|
||||||
"windows": {
|
"custom_PBA_windows_cmd": {
|
||||||
"title": "Windows command",
|
"title": "Windows command",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
"description": "Windows command to execute after breaching"
|
"description": "Windows command to be executed after breaching."
|
||||||
},
|
},
|
||||||
"windows_file": {
|
"PBA_windows_file": {
|
||||||
"title": "Windows file",
|
"title": "Windows file",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "data-url",
|
"format": "data-url",
|
||||||
"description": "File to be executed after breaching. "
|
"description": "File to be executed after breaching. "
|
||||||
"If you want custom execution behavior, "
|
"If you want custom execution behavior, "
|
||||||
"specify it in 'Windows command' field. "
|
"specify it in 'Windows post breach command' field. "
|
||||||
|
"Reference your file by filename."
|
||||||
},
|
},
|
||||||
"windows_file_info": {
|
"PBA_windows_filename": {
|
||||||
"title": "Windows PBA file info",
|
"title": "Windows PBA filename",
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"name": {
|
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": ""
|
"default": ""
|
||||||
},
|
},
|
||||||
"size": {
|
"PBA_linux_filename": {
|
||||||
"type": "string",
|
"title": "Linux PBA filename",
|
||||||
"default": "0"
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"linux_file_info": {
|
|
||||||
"title": "Linux PBA file info",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"name": {
|
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": ""
|
"default": ""
|
||||||
},
|
},
|
||||||
"size": {
|
|
||||||
"type": "string",
|
|
||||||
"default": "0"
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"description": "List of actions the Monkey will run post breach"
|
|
||||||
},
|
|
||||||
"self_delete_in_cleanup": {
|
"self_delete_in_cleanup": {
|
||||||
"title": "Self delete on cleanup",
|
"title": "Self delete on cleanup",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|
|
@ -156,7 +156,7 @@ class ReportService:
|
||||||
'domain_name': monkey['domain_name'],
|
'domain_name': monkey['domain_name'],
|
||||||
'exploits': list(set(
|
'exploits': list(set(
|
||||||
[ReportService.EXPLOIT_DISPLAY_DICT[exploit['exploiter']] for exploit in monkey['exploits'] if
|
[ReportService.EXPLOIT_DISPLAY_DICT[exploit['exploiter']] for exploit in monkey['exploits'] if
|
||||||
exploit['result']])),
|
exploit['result']]))
|
||||||
}
|
}
|
||||||
for monkey in exploited]
|
for monkey in exploited]
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,37 @@ import 'filepond/dist/filepond.min.css';
|
||||||
class ConfigurePageComponent extends AuthComponent {
|
class ConfigurePageComponent extends AuthComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
this.PBAwindowsPond = null;
|
||||||
|
this.PBAlinuxPond = null;
|
||||||
this.currentSection = 'basic';
|
this.currentSection = 'basic';
|
||||||
this.currentFormData = {};
|
this.currentFormData = {};
|
||||||
this.sectionsOrder = ['basic', 'basic_network', 'monkey', 'cnc', 'network', 'exploits', 'internal'];
|
this.sectionsOrder = ['basic', 'basic_network', 'monkey', 'cnc', 'network', 'exploits', 'internal'];
|
||||||
|
this.uiSchema = {
|
||||||
|
behaviour: {
|
||||||
|
custom_PBA_linux_cmd: {
|
||||||
|
"ui:widget": "textarea",
|
||||||
|
"ui:emptyValue": ""
|
||||||
|
},
|
||||||
|
PBA_linux_file: {
|
||||||
|
"ui:widget": this.PBAlinux
|
||||||
|
},
|
||||||
|
custom_PBA_windows_cmd: {
|
||||||
|
"ui:widget": "textarea",
|
||||||
|
"ui:emptyValue": ""
|
||||||
|
},
|
||||||
|
PBA_windows_file: {
|
||||||
|
"ui:widget": this.PBAwindows
|
||||||
|
},
|
||||||
|
PBA_linux_filename: {
|
||||||
|
classNames: "linux-pba-file-info",
|
||||||
|
"ui:emptyValue": ""
|
||||||
|
},
|
||||||
|
PBA_windows_filename: {
|
||||||
|
classNames: "windows-pba-file-info",
|
||||||
|
"ui:emptyValue": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
// set schema from server
|
// set schema from server
|
||||||
this.state = {
|
this.state = {
|
||||||
schema: {},
|
schema: {},
|
||||||
|
@ -117,16 +143,17 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
|
|
||||||
removePBAfiles(){
|
removePBAfiles(){
|
||||||
// We need to clean files from widget, local state and configuration (to sync with bac end)
|
// We need to clean files from widget, local state and configuration (to sync with bac end)
|
||||||
if (this.hasOwnProperty('PBAlinuxPond') && this.PBAwindowsPond !== null){
|
if (this.PBAwindowsPond !== null){
|
||||||
this.PBAlinuxPond.removeFile();
|
|
||||||
this.PBAwindowsPond.removeFile();
|
this.PBAwindowsPond.removeFile();
|
||||||
}
|
}
|
||||||
|
if (this.PBAlinuxPond !== null){
|
||||||
|
this.PBAlinuxPond.removeFile();
|
||||||
|
}
|
||||||
let request_options = {method: 'DELETE',
|
let request_options = {method: 'DELETE',
|
||||||
headers: {'Content-Type': 'text/plain'}};
|
headers: {'Content-Type': 'text/plain'}};
|
||||||
this.authFetch('/api/fileUpload/PBAlinux', request_options);
|
this.authFetch('/api/fileUpload/PBAlinux', request_options);
|
||||||
this.authFetch('/api/fileUpload/PBAwindows', request_options);
|
this.authFetch('/api/fileUpload/PBAwindows', request_options);
|
||||||
this.setState({PBAlinuxFile: []});
|
this.setState({PBAlinuxFile: [], PBAwinFile: []});
|
||||||
this.setState({PBAwinFile: []});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onReadFile = (event) => {
|
onReadFile = (event) => {
|
||||||
|
@ -197,65 +224,43 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
|
|
||||||
getWinPBAfile(){
|
getWinPBAfile(){
|
||||||
if (this.state.PBAwinFile.length !== 0){
|
if (this.state.PBAwinFile.length !== 0){
|
||||||
return ConfigurePageComponent.getPBAfile(this.state.PBAwinFile[0], true)
|
return ConfigurePageComponent.getMockPBAfile(this.state.PBAwinFile[0])
|
||||||
} else if (this.state.configuration.monkey.behaviour.custom_post_breach.windows_file_info.name){
|
} else if (this.state.configuration.monkey.behaviour.PBA_windows_filename){
|
||||||
return ConfigurePageComponent.getPBAfile(this.state.configuration.monkey.behaviour.custom_post_breach.windows_file_info)
|
return ConfigurePageComponent.getFullPBAfile(this.state.configuration.monkey.behaviour.PBA_windows_filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getLinuxPBAfile(){
|
getLinuxPBAfile(){
|
||||||
if (this.state.PBAlinuxFile.length !== 0){
|
if (this.state.PBAlinuxFile.length !== 0){
|
||||||
return ConfigurePageComponent.getPBAfile(this.state.PBAlinuxFile[0], true)
|
return ConfigurePageComponent.getMockPBAfile(this.state.PBAlinuxFile[0])
|
||||||
} else if (this.state.configuration.monkey.behaviour.custom_post_breach.linux_file_info.name) {
|
} else if (this.state.configuration.monkey.behaviour.PBA_linux_filename) {
|
||||||
return ConfigurePageComponent.getPBAfile(this.state.configuration.monkey.behaviour.custom_post_breach.linux_file_info)
|
return ConfigurePageComponent.getFullPBAfile(this.state.configuration.monkey.behaviour.PBA_linux_filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static getPBAfile(fileSrc, isMock=false){
|
static getFullPBAfile(filename){
|
||||||
let PBAfile = [{
|
let pbaFile = [{
|
||||||
source: fileSrc.name,
|
source: filename,
|
||||||
options: {
|
options: {
|
||||||
type: 'limbo'
|
type: 'limbo'
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
if (isMock){
|
return pbaFile
|
||||||
PBAfile[0].options.file = fileSrc
|
|
||||||
}
|
}
|
||||||
return PBAfile
|
|
||||||
|
static getMockPBAfile(mockFile){
|
||||||
|
let pbaFile = [{
|
||||||
|
source: mockFile.name,
|
||||||
|
options: {
|
||||||
|
type: 'limbo'
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
pbaFile[0].options.file = mockFile;
|
||||||
|
return pbaFile
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let displayedSchema = {};
|
let displayedSchema = {};
|
||||||
const uiSchema = {
|
|
||||||
behaviour: {
|
|
||||||
custom_post_breach: {
|
|
||||||
linux: {
|
|
||||||
"ui:widget": "textarea",
|
|
||||||
"ui:emptyValue": ""
|
|
||||||
},
|
|
||||||
linux_file: {
|
|
||||||
"ui:widget": this.PBAlinux
|
|
||||||
},
|
|
||||||
windows: {
|
|
||||||
"ui:widget": "textarea",
|
|
||||||
"ui:emptyValue": ""
|
|
||||||
},
|
|
||||||
windows_file: {
|
|
||||||
"ui:widget": this.PBAwindows
|
|
||||||
},
|
|
||||||
linux_file_info: {
|
|
||||||
classNames: "linux-pba-file-info",
|
|
||||||
name:{ "ui:emptyValue": ""},
|
|
||||||
size:{ "ui:emptyValue": "0"}
|
|
||||||
},
|
|
||||||
windows_file_info: {
|
|
||||||
classNames: "windows-pba-file-info",
|
|
||||||
name:{ "ui:emptyValue": ""},
|
|
||||||
size:{ "ui:emptyValue": "0"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (this.state.schema.hasOwnProperty('properties')) {
|
if (this.state.schema.hasOwnProperty('properties')) {
|
||||||
displayedSchema = this.state.schema['properties'][this.state.selectedSection];
|
displayedSchema = this.state.schema['properties'][this.state.selectedSection];
|
||||||
displayedSchema['definitions'] = this.state.schema['definitions'];
|
displayedSchema['definitions'] = this.state.schema['definitions'];
|
||||||
|
@ -281,7 +286,7 @@ class ConfigurePageComponent extends AuthComponent {
|
||||||
}
|
}
|
||||||
{ this.state.selectedSection ?
|
{ this.state.selectedSection ?
|
||||||
<Form schema={displayedSchema}
|
<Form schema={displayedSchema}
|
||||||
uiSchema={uiSchema}
|
uiSchema={this.uiSchema}
|
||||||
formData={this.state.configuration[this.state.selectedSection]}
|
formData={this.state.configuration[this.state.selectedSection]}
|
||||||
onSubmit={this.onSubmit}
|
onSubmit={this.onSubmit}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
|
|
Loading…
Reference in New Issue