async def update_relationships(self, request: web.Request): knowledge_svc_handle = self._api_manager.knowledge_svc relationship_data = await self._api_manager.extract_data(request) if 'criteria' in relationship_data and 'updates' in relationship_data: try: await knowledge_svc_handle.update_relationship( criteria=relationship_data['criteria'], updates=relationship_data['updates']) temp = await self._api_manager.copy_object( relationship_data['criteria']) for k in relationship_data['updates']: if isinstance(relationship_data['updates'][k], dict): handle = dict() if k in relationship_data['criteria'] and \ isinstance(relationship_data['criteria'][k], dict): handle = relationship_data['criteria'][k] for j in relationship_data['updates'][k]: handle[j] = relationship_data['updates'][k][j] temp[k] = handle else: temp[k] = relationship_data['updates'][k] store = await knowledge_svc_handle.get_relationships( criteria=temp) resp = await self._api_manager.verify_relationship_integrity( store) return web.json_response(dict(updated=resp)) except Exception as e: error_msg = f'Encountered issue updating relationship {relationship_data} - {e}' self.log.warning(error_msg) raise JsonHttpBadRequest(error_msg) raise JsonHttpBadRequest( "Need a 'criteria' to match on and 'updates' to apply.")
def validate_link_data(self, link_data: dict): if not link_data.get('executor'): raise JsonHttpBadRequest( '\'executor\' is a required field for link creation.') if not link_data['executor'].get('name'): raise JsonHttpBadRequest( '\'name\' is a required field for link executor.') if not link_data['executor'].get('command'): raise JsonHttpBadRequest( '\'command\' is a required field for link executor.') if not link_data.get('paw'): raise JsonHttpBadRequest( '\'paw\' is a required field for link creation.')
async def add_relationships(self, request: web.Request): knowledge_svc_handle = self._api_manager.knowledge_svc relationship_data = await self._api_manager.extract_data(request) try: origin_target = WILDCARD_STRING new_relationship = Relationship.load(relationship_data) if 'origin' in relationship_data: origin_target = relationship_data['origin'] else: new_relationship.origin = origin_target shorthand = new_relationship.shorthand new_relationship.source.relationships = [shorthand] new_relationship.source.source = origin_target new_relationship.source.origin_type = OriginType.USER if 'target' in relationship_data: new_relationship.target.source = origin_target new_relationship.target.origin_type = OriginType.USER new_relationship.target.relationships = [shorthand] await knowledge_svc_handle.add_fact(new_relationship.target) await knowledge_svc_handle.add_fact(new_relationship.source) await knowledge_svc_handle.add_relationship(new_relationship) store = await knowledge_svc_handle.get_relationships( criteria=dict(source=new_relationship.source, edge=new_relationship.edge if 'edge' in relationship_data else None, target=new_relationship.target if 'target' in relationship_data else None, origin=origin_target)) resp = await self._api_manager.verify_relationship_integrity(store) return web.json_response(dict(added=resp)) except Exception as e: error_msg = f'Encountered issue saving relationship {relationship_data} - {e}' self.log.warning(error_msg) raise JsonHttpBadRequest(error_msg)
async def update_operation_link(self, operation_id: str, link_id: str, link_data: dict, access: BaseWorld.Access): operation = await self.get_operation_object(operation_id, access) link = self.search_operation_for_link(operation, link_id) if link.access not in access['access']: raise JsonHttpForbidden( f'Cannot update link {link_id} due to insufficient permissions.' ) if link.is_finished() or link.can_ignore(): raise JsonHttpForbidden( f'Cannot update a finished link: {link_id}') if link_data.get('command'): command_str = link_data.get('command') link.executor.command = command_str link.ability = self.build_ability(link_data.get('ability', {}), link.executor) link.command = self._encode_string(command_str) if link_data.get('status'): link_status = link_data['status'] if not link.is_valid_status(link_status): raise JsonHttpBadRequest( f'Cannot update link {link_id} due to invalid link status.' ) link.status = link_status return link.display
async def create_potential_link(self, operation_id: str, data: dict, access: BaseWorld.Access): self.validate_link_data(data) operation = await self.get_operation_object(operation_id, access) agent = await self.get_agent(operation, data) if data['executor']['name'] not in agent.executors: raise JsonHttpBadRequest( f'Agent {agent.paw} missing specified executor') encoded_command = self._encode_string( agent.replace(self._encode_string(data['executor']['command']), file_svc=self.services['file_svc'])) executor = self.build_executor(data=data.pop('executor', {}), agent=agent) ability = self.build_ability(data=data.pop('ability', {}), executor=executor) link = Link.load( dict(command=encoded_command, paw=agent.paw, ability=ability, executor=executor, status=operation.link_status(), score=data.get('score', 0), jitter=data.get('jitter', 0), cleanup=data.get('cleanup', 0), pin=data.get('pin', 0), host=agent.host, deadman=data.get('deadman', False), used=data.get('used', []), relationships=data.get('relationships', []))) link.apply_id(agent.host) await operation.apply(link) return link.display
async def _error_if_object_with_id_exists(self, obj_id: str): """Throw an error if an object (of the same type) exists with the given id""" if obj_id: search = {self.id_property: obj_id} if self._api_manager.find_object(self.ram_key, search): raise JsonHttpBadRequest( f'{self.description.capitalize()} with given id already exists: {obj_id}' )
async def validate_operation_state(self, data: dict, existing: Operation = None): if not existing: if data.get('state') in Operation.get_finished_states(): raise JsonHttpBadRequest('Cannot create a finished operation.') elif data.get('state') not in Operation.get_states(): raise JsonHttpBadRequest('state must be one of {}'.format( Operation.get_states())) else: if await existing.is_finished(): raise JsonHttpBadRequest( 'This operation has already finished.') elif 'state' in data and data.get( 'state') not in Operation.get_states(): raise JsonHttpBadRequest('state must be one of {}'.format( Operation.get_states()))
async def extract_data(request: web.Request): fact_data = None if request.body_exists: try: fact_data = await request.json() except JSONDecodeError as e: raise JsonHttpBadRequest('Received invalid json', details=e) return fact_data
async def update_facts(self, request: web.Request): knowledge_svc_handle = self._api_manager.knowledge_svc fact_data = await self._api_manager.extract_data(request) if 'criteria' in fact_data and 'updates' in fact_data: try: await knowledge_svc_handle.update_fact(criteria=fact_data['criteria'], updates=fact_data['updates']) temp = await self._api_manager.copy_object(fact_data['criteria']) for k in fact_data['updates']: temp[k] = fact_data['updates'][k] store = await knowledge_svc_handle.get_facts(criteria=temp) resp = await self._api_manager.verify_fact_integrity(store) return web.json_response(dict(updated=resp)) except Exception as e: error_msg = f'Encountered issue updating fact {fact_data} - {e}' self.log.warning(error_msg) raise JsonHttpBadRequest(error_msg) raise JsonHttpBadRequest("Need a 'criteria' to match on and 'updates' to apply.")
async def extract_data(request: web.Request): fact_data = None raw_body = await request.read() if raw_body: try: fact_data = json.loads(raw_body) except JSONDecodeError as e: raise JsonHttpBadRequest('Received invalid json', details=e) return fact_data
async def get_facts(self, request: web.Request): fact_data = await self._api_manager.extract_data(request) resp = [] if fact_data: try: resp = await self._find_and_verify_facts(fact_data) except Exception as e: error_msg = f'Encountered issue retrieving fact {fact_data} - {e}' self.log.warning(error_msg) raise JsonHttpBadRequest(error_msg) return web.json_response(dict(found=resp))
async def get_facts_by_operation_id(self, request: web.Request): operation_id = request.match_info.get('operation_id') fact_data = {'source': operation_id} resp = [] if fact_data: try: resp = await self._find_and_verify_facts(fact_data) except Exception as e: error_msg = f'Encountered issue retrieving facts associated with operation {operation_id} - {e}' self.log.warning(error_msg) raise JsonHttpBadRequest(error_msg) return web.json_response(dict(found=resp))
async def delete_relationships(self, request: web.Request): knowledge_svc_handle = self._api_manager.knowledge_svc relationship_data = await self._api_manager.extract_data(request) if relationship_data: try: store = await knowledge_svc_handle.get_relationships(criteria=relationship_data) await knowledge_svc_handle.delete_relationship(criteria=relationship_data) resp = await self._api_manager.verify_relationship_integrity(store) return web.json_response(dict(removed=resp)) except Exception as e: self.log.warning(f'Encountered issue removing relationship {relationship_data} - {e}') raise JsonHttpBadRequest('Invalid relationship data was provided.')
async def get_relationships(self, request: web.Request): knowledge_svc_handle = self._api_manager.knowledge_svc relationship_data = await self._api_manager.extract_data(request) resp = [] if relationship_data: try: RelationshipSchema(partial=True).load(relationship_data) store = await knowledge_svc_handle.get_relationships(criteria=relationship_data) resp = await self._api_manager.verify_relationship_integrity(store) except Exception as e: error_msg = f'Encountered issue retrieving relationship {relationship_data} - {e}' self.log.warning(error_msg) raise JsonHttpBadRequest(error_msg) return web.json_response(dict(found=resp))
async def add_facts(self, request: web.Request): knowledge_svc_handle = self._api_manager.knowledge_svc fact_data = await self._api_manager.extract_data(request) try: new_fact = Fact.load(fact_data) if 'source' not in fact_data: new_fact.source = WILDCARD_STRING new_fact.origin_type = OriginType.USER await self._api_manager.verify_operation_state(new_fact) await knowledge_svc_handle.add_fact(new_fact) store = await knowledge_svc_handle.get_facts(criteria=dict(trait=new_fact.trait, value=new_fact.value, source=new_fact.source, origin_type=OriginType.USER)) resp = await self._api_manager.verify_fact_integrity(store) return web.json_response(dict(added=resp)) except Exception as e: error_msg = f'Encountered issue saving fact {fact_data} - {e}' self.log.warning(error_msg) raise JsonHttpBadRequest(error_msg)
def _validate_ability_data(self, create: bool, data: dict): # Correct ability_id key for ability file saving. data['id'] = data.pop('ability_id', '') # If a new ability is being created, ensure required fields present. if create: # Set ability ID if undefined if not data['id']: data['id'] = str(uuid.uuid4()) if not data.get('name'): raise JsonHttpBadRequest( f'Cannot create ability {data["id"]} due to missing name') if 'tactic' not in data: raise JsonHttpBadRequest( f'Cannot create ability {data["id"]} due to missing tactic' ) if not data.get('executors'): raise JsonHttpBadRequest( f'Cannot create ability {data["id"]}: at least one executor required' ) # Validate ID, used for file creation validator = re.compile(r'^[a-zA-Z0-9-_]+$') if 'id' in data and not validator.match(data['id']): raise JsonHttpBadRequest( f'Invalid ability ID {data["id"]}. IDs can only contain ' 'alphanumeric characters, hyphens, and underscores.') # Validate tactic, used for directory creation, lower case if present if 'tactic' in data: if not validator.match(data['tactic']): raise JsonHttpBadRequest( f'Invalid ability tactic {data["tactic"]}. Tactics can only contain ' 'alphanumeric characters, hyphens, and underscores.') data['tactic'] = data['tactic'].lower() if 'executors' in data and not data.get('executors'): raise JsonHttpBadRequest( f'Cannot create ability {data["id"]}: at least one executor required' ) if 'name' in data and not data.get('name'): raise JsonHttpBadRequest( f'Cannot create ability {data["id"]} due to missing name')