cyst.api.environment.environment

class cyst.api.environment.environment.Environment

Bases: ABC

The Environment provides a highest-level interface to controlling the simulation. It consists of a number of lower-level interfaces that provide a specific functionality.

abstract property configuration: EnvironmentConfiguration

This interface is a collection of configuration interfaces for the environment, that are split according to their general functionality.

Return type:

EnvironmentConfiguration

abstract property control: EnvironmentControl

This interface provides mechanisms to control the execution of actions within the simulation environment.

Return type:

EnvironmentControl

abstract property messaging: EnvironmentMessaging

This interface enables creating and sending of messages within simulation.

Return type:

EnvironmentMessaging

abstract property resources: EnvironmentResources

This interface gives access to resources, such as actions or exploits.

Return type:

EnvironmentResources

abstract property platform_interface: PlatformInterface

This interface provides means for execution platforms to execute actions and to notify the results of actions.

Return type:

PlatformInterface

abstract property infrastructure: EnvironmentInfrastructure

This interface provides access to environment resources that are aimed at behavioral models and to applications utilizing CYST.

Return type:

EnvironmentInfrastructure

abstract property platform: Platform

Returns the underlying execution platform, which is CYST running on.

Return type:

Platform

abstractmethod configure(*config_item: ConfigItem, parameters: Dict[str, Any] | None = None) Environment

Configures the environment, according to provided configuration items. This function can be called repeatedly, however, each subsequent call replaces the previous configuration. Therefore, a configuration must be done at once and every later change in the environment setup must be done through the cyst.api.environment.configuration.EnvironmentConfiguration interface.

Parameters:
  • config_item (ConfigItem) – One or more configuration items. The number of items can be arbitrary and it is not order-dependent.

  • parameters (Dict[str, Any] | None) – Name-value mapping for configuration that supports parametrization. If none is provided, despite parameterizable configuration, default values are used.

Returns:

The configured environment. Used this way for the shorthand form:

e = Environment.create().configure(*config, parameters)
classmethod create(platform: str | PlatformSpecification | None = None, run_id: str = '') Environment

Creates a new instance of the environment. A program using CYST can use multiple environments, however, each simulation run happens only in the context of one environment.

Parameters:
  • platform (Optional[Union[str, PlatformSpecification]) – A specification of a platform to use as a backend for CYST run. By default, a CYST simulation using a simulated time is used.

  • run_id (str) – The unique id of the current run. If a non-unique id is selected, it may produce unwanted results when saving the data to a data store.

Returns:

An environment instance.

cyst.api.environment.clock

class cyst.api.environment.clock.Clock

Bases: ABC

Clock interface provides access to time management of a given platform.

abstractmethod current_time() float

Returns a current time of a platform as an offset from a specific time in the past. In case of a discrete simulation platform, it will likely return a whole number. For other environments, this will be a fractional number (aka. python’s time() function).

Returns:

Current time as an offset.

Added in version 0.6.0.

abstractmethod real_time() datetime

Returns a current time of a platform converted to a real date-time information. In case of a discrete simulation platform this entails a conversion of time offset to a real time. In case o a real-time environment this will likely be only a reading of a system clock.

Returns:

Current time as a datetime structure.

Added in version 0.6.0.

abstractmethod timeout(callback: ActiveService | Callable[[Message], Tuple[bool, int]], delay: float, parameter: Any = None) None

Schedule a timeout message. This acts like a time callback and enables inclusion of any kind of data.

Parameters:
  • callback – Either a service, which should receive the timeout message, or an arbitrary callback.

  • delay – The duration of the timeout in simulation time.

  • parameter – The included data. They will not be modified.

Returns:

None

Changed in version 0.6.0: Changed delay type to float and added support for generic callbacks.

cyst.api.environment.configuration

This module provides a set of interfaces, which enable procedural configuration of simulation environments. Unlike the cyst.api.configuration module, which declaratively configures the environment only at the initialization, this module enables configuration and manipulation of the environment in all states.

While it is internally used to implement the declarative configuration, it is mainly used by the behavioral models to implement changes in the environment in the reaction to agents’ actions.

class cyst.api.environment.configuration.EnvironmentConfiguration

Bases: ABC

This interface is a collection of configuration interfaces for the environment, that are split according to their general functionality.

abstract property general: GeneralConfiguration

General configuration enables retrieval of objects and their configurations. As such it enables manipulation with all objects that are present in the simulation run.

Return type:

GeneralConfiguration

abstract property node: NodeConfiguration

Node configuration enables creation and manipulation with nodes and routers.

Return type:

NodeConfiguration

abstract property service: ServiceConfiguration

Service configuration enables management of passive and active services.

Return type:

ServiceConfiguration

abstract property network: NetworkConfiguration

Network configuration enables placing nodes inside the topology and manipulation with connection and sessions. Currently, it is mostly additive, but it will include all manipulation options in the future.

Return type:

NetworkConfiguration

abstract property exploit: ExploitConfiguration

This interface provides means to manipulate with exploits.

Return type:

ExploitConfiguration

abstract property action: ActionConfiguration

Action configuration enables creation of action parameter domains. While the actions are fully declarative in their description and their semantics stem from the interpretation given by behavioral models, action parameters enable fine-tuning of actions, that is accessible to automatic tools in a uniform manner.

Return type:

ActionConfiguration

abstract property access: AccessConfiguration

Access configuration enables manipulation of authentication and authorization primitives, creation of access schemes and evaluation of authentication tokens.

Return type:

AccessConfiguration

abstract property physical: PhysicalConfiguration

Physical configuration enables manipulation with physical locations, their assets, and users within them.

Return type:

PhysicalConfiguration

class cyst.api.environment.configuration.GeneralConfiguration

Bases: ABC

General configuration enables retrieval of objects and their configurations. As such it enables manipulation with all objects that are present in the simulation run.

abstractmethod get_configuration() List[ConfigItem]

Get the entire configuration of the environment in form of a list containing top level configuration objects, i.e., nodes, connections, and exploits. There are no inter-object references, they are all resolved, so there is a potential duplication.

Returns:

A complete environment configuration.

abstractmethod save_configuration(indent: int | None) str

Serializes a configuration into a string representation, that can be saved and later loaded and passed to the configure() method.

Parameters:

indent (Optional[int]) – The indentation level for pretty-printing. If not provided, the configuration is serialized onto one line. If indent is set to 0, only line breaks are inserted. If indent is set to 1 or more the text is indented accordingly.

Returns:

A string representation of the environment’s configuration.

abstractmethod load_configuration(config: str) List[ConfigItem]

Deserializes a string configuration into corresponding configuration items. These are currently not guaranteed to work across versions.

Parameters:

config (str) – The serialized configuration.

Returns:

A list of configuration items that can be fed to the configure() function.

abstractmethod get_configuration_by_id(id: str, configuration_type: Type[ConfigurationObjectType]) ConfigurationObjectType

Get a …Config object by ID.

Parameters:
  • id (str) – The ID of the configuration object.

  • configuration_type (Type[TypeVar('ConfigurationObjectType')]) – A type of the configuration item to get. While not technically necessary, given the state of Python’s type system, it is there to satisfy static typing inspection.

Returns:

A configuration object with given ID.

abstractmethod get_object_by_id(id: str, object_type: Type[ObjectType]) ObjectType

Get this back… :param id: :param object_type: :return:

class cyst.api.environment.configuration.NodeConfiguration

Bases: ABC

Node configuration enables creation and manipulation with nodes and routers.

abstractmethod create_node(id: str, ip: str | IPAddress = '', mask: str = '', shell: Service = None) Node

Create a node within a simulation. The node itself is empty and must be filled with services before it can interact with the environment. The same goes for its network connection, unless provided in the constructor.

Parameters:
  • id (str) – A unique identification of the node within the simulation. Setting a duplicate ID would result in an exception.

  • ip (Union[str, IPAddress]) – An IP address this node should use. Setting this parameter automatically creates the appropriate network interface. The address can be passed as a string or as an IPAddress, into which it is internally converted from the string.

  • mask (str) – A network mask to use. This will be used for setting the gateway and routing policy on the node. If the mask is not selected, it gets negotiated during a connection with the router (TODO: check).

  • shell (Service) – A service that will be acting as a system shell.

Returns:

An instance of a node in the simulation.

abstractmethod create_router(id: str, messaging: EnvironmentMessaging) Node

Create a router within simulation. The router is a special type of node, which enables message forwarding and network partitioning.

Parameters:
  • id (str) – A unique identification of the node within simulation. Setting a duplicate ID would result in an exception.

  • messaging (EnvironmentMessaging) – A reference to EnvironmentMessaging

Returns:

An instance of a router in the simulation.

abstractmethod create_port(ip: str | IPAddress = '', mask: str = '', index: int = 0, id: str = '') Interface

Create a network port, which can then be used to connect routers to routers.

Parameters:
  • ip (Union[str, IPAddress]) – An IP address of the port. If not set, it will be a port without IP and the routing will have to be explicitly specified.

  • mask (str) – A network mask of the port. It is used to signal the network range serviced by the router.

  • index (int) – An index the port should have. If a value -1 is selected, the interface is assigned a lowest possible index.

  • id (str) – A unique identification of the interface within the simulation. If no value is provided, the system will generate a unique one.

Returns:

An instance of a network port.

abstractmethod create_interface(ip: str | IPAddress = '', mask: str = '', index: int = 0, id: str = '') Interface

Create a network interface, which can then be used to connect nodes to routers or routers to routers.

Parameters:
  • ip (Union[str, IPAddress]) – An IP address of the interface. If not set, it will act as a DHCP interface for nodes and an IP-less port for routers.

  • mask (str) – A network mask of the interface. In case of a node interface it will be used for setting the gateway and routing policy on the node. If the mask is not selected, it gets negotiated during a connection with the router. In case of router port, the mask is used to determine the pool for DHCP addresses.

  • index (int) – An index the port should have, once assigned to a node. If a value -1 is selected, the interface is assigned a lowest possible index.

  • id (str) – A unique identification of the interface within the simulation. If no value is provided, the system will generate a unique one.

Returns:

An instance of a network interface.

abstractmethod create_route(net: IPNetwork, port: int, metric: int, id: str = '') Route

Create a route, which determines through which port messages directed to particular network get sent.

Parameters:
  • net (IPNetwork) – A destination network, which is being routed through the specified port.

  • port (int) – A port index. If a nonexistent port is selected, it will raise an exception, when a message will be routed through it.

  • metric (int) – A metric for the route. Routes with lower metric get selected when there is an overlap in their networks. In case of metric equivalence, more specific routes are selected.

  • id (str) – A unique identification of the route within the simulation. If no value is provided, the system will generate a unique one.

Returns:

A route to be used in router.

abstractmethod add_interface(node: Node, interface: Interface, index: int = -1) int

Assigns a network interface instance to a node.

Parameters:
  • node (Node) – A node instance to assign the interface to.

  • interface (Interface) – An interface instance to be assigned.

  • index – An index to give the interface. If left at -1, a lowest possible index is given to the interface. If an already occupied index is given, an exception is thrown.

Returns:

An index, where the interface was assigned.

abstractmethod set_interface(interface: Interface, ip: str | IPAddress = '', mask: str = '') None

Sets parameters of an interface instance.

Parameters:
  • interface (Interface) – The instance of the interface.

  • ip (Union[str, IPAddress]) – A new IP address to use. Setting this to an empty string will effectively erase the IP address from the interface. In that case, it can only work in routers with explicit routing.

  • mask (str) – A new network mask.

Returns:

None

abstractmethod add_service(node: Node, *service: Service) None

Add a service to the node. The engine expects that there aren’t two services of the same name on one node. In such case, the effects are undefined.

Parameters:
  • node (Node) – An instance of a node to assign the service to.

  • service (Service) – One or more service instances to assign.

Returns:

None

abstractmethod remove_service(node: Node, *service: Service) None

Remove a service from the node.

Parameters:
  • node (Node) – An instance of a node to remove the service from.

  • sertice – One or more service instances to remove.

Returns:

None

abstractmethod set_shell(node: Node, service: Service) None

Set given service as a shell. Unlike the method add_service, set_shell does not produce undefined results, when a service already present at the node is set as a shell.

Parameters:
  • node (Node) – An instance of a node to set shell at.

  • service (Service) – An instance of a service to set.

Returns:

None

abstractmethod add_traffic_processor(node: Node, processor: ActiveService) None

Add a service to a traffic processor queue. Traffic processors process messages incoming into a node before any evaluation takes place. There can be multiple traffic processors on one node and their order depends on the order of add_traffic_processor calls. The first one added, processes the traffic first, and so on…

Any active service can be set as a traffic processor. Traffic processors are free to change the contents of messages and unless they return False from their process_message() function that message is passed to next processors or to the node.

Parameters:
  • node (Node) – An instance of the node where traffic processor should be added.

  • processor (ActiveService) – An instance of an active service which will act as a traffic processor.

Returns:

None

abstractmethod add_route(node: Node, *route: Route) None

Add a network route to a router.

Parameters:
  • node (Node) – An instance of the router. Although the type is set as a Node here, this function can only be called on routers. Trying to assign a route to an ordinary node would do nothing.

  • route (Route) – On or more network routes to add.

Returns:

None

abstractmethod add_routing_rule(node: Node, rule: FirewallRule) None

Adds a rule for source-destination routing. This effectively bypasses the routing rules and enables custom port forwarding.

TODO

This is only temporary - first, it is leaking implementation detail to outside and second, it is completely stupid, as router should be a designated active service and should provide configuration interface.

Parameters:
  • node (Node) – An instance of a router. Although the type is set as a Node here, this function can only be called on routers. Trying to add a routing rule to an ordinary node would do nothing.

  • rule (FirewallRule) – A firewall rule, which coincidentally has all the necessary ingredients for implementation of source-destination routing. See the TODO on why this is a stupid idea.

Returns:

None

abstractmethod set_routing_policy(node: Node, policy: FirewallPolicy) None

Sets a default routing policy for source-destination routing.

The same comment regarding the stupidity of this interface applies as an function add_routing_rule().

Parameters:
  • node – An instance of a router. Although the type is set as a Node here, this function can only be called on routers. Trying to assign a routing policy to an ordinary node would do nothing.

  • policy (FirewallPolicy) – A firewall policy, which coincidentally has all the necessary ingredients for implementation of source-destination routing policy. See the add_routing_rule() on why this is a stupid idea.

Returns:

None

abstractmethod list_routes(node: Node) List[Route]

List all the routes currently set at a router.

TODO

This will be changed in the future, after router is implemented as an active service with its own interface.

Parameters:

node (Node) – The router to get routes from. Although the type is set as a Node here, this function can only be called on routers. Trying to list routes from an ordinary node would do nothing.

Returns:

A list of routes.

class cyst.api.environment.configuration.ServiceConfiguration

Bases: ABC

Service configuration enables management of passive and active services.

abstractmethod create_active_service(type: str, owner: str, name: str, node: Node, service_access_level: AccessLevel = AccessLevel.LIMITED, configuration: Dict[str, Any] | None = None, id: str = '') Service | None

Creates an active service. These include anything from traffic processors to agents.

Parameters:
  • type (str) – A type of the service, which is a unique identification among services.

  • id (str) – An ID that will be given to the service and which must be unique within the simulation environment.

  • owner (str) – An identity that will own the service at the node. While it should be an identity that is present at the node, this is currently not controlled in any way, because the whole identity business is not yet sorted out.

  • name (str) – The name of the service as it was registered into the system. (see cyst.api.host.service.ActiveServiceDescription)

  • node (Node) – An instance of a node where the service will reside in the end. There is a technical limitation that lies behind this requirement. If it weren’t there, everything would be done in the add_service() call.

  • service_access_level (AccessLevel) – An access level for the service. This mostly concerns the resources of the Node it can access.

  • configuration (Optional[Dict[str, Any]]) – A dictionary of configuration parameters. There are no limitations to the param/value pairs, as they are directly passed to function creation function. (see cyst.api.host.service.ActiveServiceDescription)

  • id – An ID that will be given to the service and which must be unique within the simulation environment. If no ID is provided a unique one is generated.

Returns:

An instance of an active service, or null if failed to create one.

abstractmethod get_service_interface(service: ActiveService, control_interface_type: Type[ActiveServiceInterfaceType]) ActiveServiceInterfaceType

Active services can provide an interface, which enables a finer control over their function than a rather limited cyst.api.host.service.ActiveService interface.

Due to python’s handling of types and interfaces, you can always just call the functions of the service directly, without going through the hassle of interface acquisition, but such approach would not pass through a static verification.

Example:

from cyst.api.environment.environment import Environment
from cyst.services.scripted_attacker.main import ScriptedAttackerControl

env = Environment.create()
node = env.configuration.node.create_node("node1")
service = env.configuration.service.create_active_service("attacker1", "attacker", "scripted_attacker", node)
ctrl = env.configuration.service.get_service_interface(service.active_service, ScriptedAttackerControl)

ctrl.execute_action(...)
Parameters:
  • service (ActiveService) – An instance of an active service. Please consult the example and the Service interface to prevent unwanted surprises (.active_service).

  • control_interface_type (Type[TypeVar('ActiveServiceInterfaceType')]) – A type of the interface that should be obtained from the service. Note that a service can provide multiple interfaces.

Returns:

An interface of a service.

abstractmethod get_service_type(service: Service) str

Returns a type of given service.

Parameters:

service (Service) – A service instance.

Returns:

A service type under which it is registered in the system, i.e., the type attribute of ActiveService, or a “PassiveService” string if it is a passive service.

abstractmethod create_passive_service(type: str, owner: str, version: str = '0.0.0', local: bool = False, service_access_level: AccessLevel = AccessLevel.LIMITED, id: str = '') Service

Create a passive service.

Parameters:
  • type (str) – A type of the service, such as lighttpd or bash. The type is relevant for mapping exploits.

  • owner (str) – An identity that will own the service at the node. While it should be an identity that is present at the node, this is currently not controlled in any way, because the whole identity business is not yet sorted out.

  • version (str) – The version of the service. The format of the version should be compatible with semantic versioning scheme. TODO: Extend the signature to Union[str, VersionInfo]

  • local (bool) – An indication, whether the service is accessible from outside the node.

  • service_access_level (AccessLevel) – An access level of the service. This mostly concerns the resources than can be accessed in case of service compromise.

  • id (str) – A unique ID within the simulation. An ID is generated if not provided.

Returns:

An instance of passive service.

abstractmethod update_service_version(service: PassiveService, version: str = '0.0.0') None

Changes the service version to a new value. This function is primarily meant as a way to simulate updates or patching of already running services.

Parameters:
  • service (PassiveService) – An instance of the passive service.

  • version (str) – A new value of the version. The format of the version should be compatible with semantic versioning scheme. TODO: Extend the signature to Union[str, VersionInfo]

Returns:

None

abstractmethod set_service_parameter(service: PassiveService, parameter: ServiceParameter, value: Any) None

Sets a parameter of an existing passive service. The parameters are chosen from a parametrization domain defined by the ServiceParameter flags.

Parameters:
  • service (PassiveService) – An instance of the passive service.

  • parameter (ServiceParameter) – A type of the parameter to set.

  • value (Any) – A value of the parameter. For allowed values, consult cyst.api.environment.configuration.ServiceParameter.

Returns:

None

abstractmethod create_data(id: str | None, owner: str, path: str, description: str) Data

Creates a data. Currently, this is a very rudimentary approach and future updates are expected to provide more sophisticated mechanisms for data description and manipulation.

Parameters:
  • id (Optional[str]) – An optional identification of the data.

  • owner (str) – An identity of the owner of the data. This should be an identity existing within the simulation (not necessarily on the node and service where the data will reside), as the access to the data should be checked against the name in the authentication token, which has to be the same as the owner. [That “should” here means that it is dependent on the implementation of behavioral models.]

  • path (str) – A path to the data, which should be unique among all data within one service.

  • description (str) – A textual description of the data, which currently enables differentiation between data instances.

Returns:

An instance of data.

abstractmethod public_data(service: PassiveService) List[Data]

Gets access to the data that should be available on the service without the need for authentication. This can be used both to get and set the data.

Parameters:

service (PassiveService) – An instance of the passive service.

Returns:

A list of public data instances on the service.

abstractmethod private_data(service: PassiveService) List[Data]

Gets access to the data that should be available on the service only after authentication. This can be used both to get and set the data.

Parameters:

service (PassiveService) – An instance of the passive service.

Returns:

A list of private data instances on the service.

abstractmethod public_authorizations(service: PassiveService) List[Authorization]

Gets access to public authorizations present at the service.

Warning

This function is deprecated and will be removed soon, as it does not conform to the current authentication/authorization framework.

Parameters:

service (PassiveService) – An instance of the passive service.

Returns:

A list of publicly available authorization instances.

abstractmethod private_authorizations(service: PassiveService) List[Authorization]

Gets access to private authorizations present at the service.

Warning

This function is deprecated and will be removed soon, as it does not conform to the current authentication/authorization framework.

Parameters:

service (PassiveService) – An instance of the passive service.

Returns:

A list of privately available authorization instances.

abstractmethod sessions(service: PassiveService) List[Session]

Gets access to sessions that are connected to the service. These sessions can either be the ones created as a part of the initialization process, or those that were created following the activities in the simulation.

Parameters:

service (PassiveService) – An instance of the passive service.

Returns:

A list of sessions connected to the service.

abstractmethod provides_auth(service: Service, auth_provider: AuthenticationProvider) None

Adds an authentication provider to a service.

TODO

This function, if it is really intended to stay, must be renamed to something better, such as add_auth_provider. Also the parameter type should be checked, because the implementation conflates Service and PassiveService.

Parameters:
  • service (Service (but should be PassiveService)) – An instance of the passive service.

  • auth_provider (AuthenticationProvider) – An authentication provider to add.

Returns:

None

abstractmethod set_scheme(service: PassiveService, scheme: AccessScheme) None

Sets and authentication scheme at the service. More on authentication schemes at the user’s documentation or in the documentation of cyst.api.logic.access.

Parameters:
Returns:

None

class cyst.api.environment.configuration.NetworkConfiguration

Bases: ABC

Network configuration enables placing nodes inside the topology and manipulation with connection and sessions. Currently, it is mostly additive, but it will include all manipulation options in the future.

abstractmethod add_node(node: Node) None

Adds a node into the topology. After a node is created, it must be added to the simulation topology. Otherwise it will not be included in messaging. Note that this is done implicitly when using declarative configuration, so this mostly concerns scenarios, such as honeypot deployment.

Parameters:

node (Node) – An instance of the node.

Returns:

None

abstractmethod add_connection(source: Node, target: Node, source_port_index: int = -1, target_port_index: int = -1, net: str = '', connection: Connection | None = None) Connection

Adds a connection between two nodes, provided each one has a network port available. If one of the nodes is router, it may trigger DHCP address assignment, depending on the settings of node interfaces.

Parameters:
  • source (Node) – An instance of the source node. Connections are currently bi-directional, so it does not matter which one is the source and which one is the destination.

  • target (Node) – An instance of the destination node.

  • source_port_index (int) – An index of the interface at the source node. If the index does not point to an existing interface, an exception will be thrown.

  • target_port_index (int) – An index of the interface at the destination node. If the index does not point to an existing interface, an exception will be thrown.

  • net (str) – A network mask specification, used by a router (if one is present) to determine a correct address range. This parameter is not necessary, if all addresses are fully specified in the interfaces.

  • connection (Connection) – An instance of a connection. This parameter enables passing a specific connection instance, which has, e.g., specific connection properties. If this parameter is omitted, then a new connection is automatically created.

Returns:

An instance of a connection that was established between selected nodes.

abstractmethod get_connections(node: Node, port_index: int | None = None) List[Connection]

Gets connections that are connected to the node at the port index, if specified.

Parameters:
  • node (Node) – An instance of the node.

  • port_index (Optional[int]) – An index of the port.

Returns:

A list of connections that are connected to the node.

abstractmethod create_session(owner: str, waypoints: List[str | Node], src_service: str | None = None, dst_service: str | None = None, parent: Session | None = None, defer: bool = False, reverse: bool = False, id: str | None = None) Session | None

Creates a fixed session in the simulated infrastructure. This session ignores the routing limitations imposed by router configuration. However, the creation mechanism checks if there exists a connection between each of session waypoints and it selects the appropriate interfaces to realize the connection through.

Parameters:
  • owner (str) – The owner of the session. This parameter is used to prevent sharing of sessions, but it will likely be deprecated in future versions, because it does not work well when appending sessions through exploiting.

  • waypoints (List[Union[str, Node]]) – A list of node instances or their IDs, which constitute the path of the session. As per the description, there has to be a connection between all subsequent tuples of waypoints.

  • src_service (Optional[str]) – An ID of the service that is the anchor at the origin of the session. This service can tear down the session at will.

  • dst_service (Optional[str]) – An ID of the service that is the anchor at the destination of the session. This service can tear down the session at will.

  • parent (Optional[Session]) – A parent session that can be optionally used as a first part/head of the session.

  • defer (bool) – I this parameter is set to False, the system attempts to create the session immediately. If set to False, the creation is deferred to the environment init phase, when all the waypoints should already be instantiated.

  • reverse (bool) – Construct the session in the reverse order, i.e., as if it was constructed as a reverse shell.

  • id (str) – An explicit ID of the session. If none is provided, it is autogenerated.

Returns:

An instance of a created session (unless deferred, than it always return null)

abstractmethod append_session(original_session: Session, appended_session: Session) Session

Creates a new session by appending a two sessions together.

Parameters:
  • original_session (Session) – The session that will serve as a beginning of the session. At its endpoint the second session will be appended.

  • appended_session (Session) – The session that will serve as the end of the session. It will be connected to the original session with its startpoint.

Returns:

A new session that is a combination of both the original ones.

abstractmethod create_session_from_message(message: Message, reverse_direction: bool = False) Session

Establishes a new session from the path the message travelled. This function is mostly used by behavioral models to create new sessions in response to messages coming from attackers.

Parameters:
  • message (Message) – An instance of the message.

  • reverse_direction (bool) – Whether the direction of the shell is reversed or not.

Returns:

A new session.

class cyst.api.environment.configuration.ExploitConfiguration

Bases: ABC

This interface provides means to manipulate with exploits.

abstractmethod create_vulnerable_service(id: str, min_version: str = '0.0.0', max_version: str = '0.0.0') VulnerableService

Create a definition of a vulnerable service, that is used for exploit specification

Parameters:
  • name (str) – A name of the service. It is an equivalent of PassiveService/PassiveServiceConfig name.

  • min_version (str) – A minimum version of the service that is vulnerable. Even though the type supports any string, using anything else than string representation of a semantic version will lead to problems.

  • max_version (str) – A maximum version of the service that is vulnerable. Even though the type supports any string, using anything else than string representation of a semantic version will lead to problems.

Returns:

A vulnerable service description.

abstractmethod create_exploit_parameter(exploit_type: ExploitParameterType, value: str = '', immutable: bool = False) ExploitParameter

Creates an exploit parameter.

Exploit parameter either represents a specification of an exploit, which can’t be modified by a user, but is taken into account by exploit evaluation mechanisms (e.g., if the impact is one user or all users), or provides a mean for a user to supply additional information necessary for full execution of the exploit (e.g., identity of a user to impersonate).

Parameters:
  • type (ExploitParameterType) – A type of the exploit parameter.

  • value (Optional[str]) – A value of the parameter.

  • immutable (bool) – A flag indicating, whether a user can change the value of the parameter.

Returns:

An instance of exploit parameter.

abstractmethod create_exploit(id: str = '', services: List[VulnerableService] = None, locality: ExploitLocality = ExploitLocality.NONE, category: ExploitCategory = ExploitCategory.NONE, *parameters: ExploitParameter) Exploit

Creates a working exploit.

If a definition of exploit exists, then a services it refers to are vulnerable, as long as their version match. Patching the service and bumping a version over the max vulnerable version is the only countermeasure.

Parameters:
  • id (str) – A unique identification of the exploit.

  • services (List[VulnerableService]) – A list of services that are vulnerable to this exploit.

  • locality (ExploitLocality) – Determines if the exploit can be used locally or remotely.

  • category (ExploitCategory) – Determines the category of the exploit.

  • parameters (Optional[List[ExploitParameter]]) – An optional list of exploit parameters.

Returns:

An instance of the exploit.

abstractmethod add_exploit(*exploits: Exploit) None

Adds an exploit to the exploit store, where it can be accessed by the services. Unless an exploit is added to the store, it cannot be used, even though it was created.

Parameters:

exploits (Exploit) – One or more exploit instances to be added.

Returns:

None

abstractmethod clear_exploits() None

Removes all exploits from exploit store.

Returns:

None

class cyst.api.environment.configuration.ActionConfiguration

Bases: ABC

Action configuration enables creation of action parameter domains. While the actions are fully declarative in their description and their semantics stem from the interpretation given by behavioral models, action parameters enable fine-tuning of actions, that is accessible to automatic tools in a uniform manner.

abstractmethod create_action_parameter_domain_any() ActionParameterDomain

Create an action parameter domain that can accept any parameter. This domain should be avoided at all costs, because it does not provide anything for automated tools to work with.

Returns:

An unbounded parameter domain.

abstractmethod create_action_parameter_domain_range(default: int, min: int, max: int, step: int = 1) ActionParameterDomain

Creates a parameter domain that is represented by a range of numbers. Ideal for expressing things like port ranges.

Parameters:
  • default (int) – A default value of the domain.

  • min (int) – A minimal range value.

  • max (int) – A maximal range value.

  • step (int) – Enables to include only every step-th number between min and max.

Returns:

A parameter domain consisting of range of numbers.

abstractmethod create_action_parameter_domain_options(default: Any, options: List[Any]) ActionParameterDomain

Creates a parameter domain that is represented by a set of possible values. The values are not limited in any way, but this enables automated tools to select from them.

Parameters:
  • default (Any) – A default value of the domain.

  • options (List[Any]) – A list of values, which constitute the domain.

Returns:

A parameter domain consisting of set of values.

class cyst.api.environment.configuration.AccessConfiguration

Bases: ABC

Access configuration enables manipulation of authentication and authorization primitives, creation of access schemes and evaluation of authentication tokens.

abstractmethod create_authentication_provider(provider_type: AuthenticationProviderType, token_type: AuthenticationTokenType, security: AuthenticationTokenSecurity, ip: IPAddress | None, timeout: int, id: str = '') AuthenticationProvider

Authentication provider represents an authentication mechanism that can be employed in services via the access scheme mechanism.

Parameters:
  • provider_type (AuthenticationProviderType) – The type of authentication provider.

  • token_type (AuthenticationTokenType) – The type of tokens that are employed by this authentication provider.

  • token_security (AuthenticationTokenSecurity) – Security mechanism applied to stored tokens.

  • ip (Optional[IPAddress]) – An optional IP address, which is intended for remote or federated providers. It represents an IP address where this provider can be accessed.

  • timeout (int) – A number of simulated time units that can elapse from the initiation of authentication exchange before the attempt is discarded as unsuccessful.

  • id (str) – A unique identification of the provider within the simulation. If no value is provided, the system will generate a unique one.

Returns:

An authentication provider instance.

abstractmethod create_authentication_token(type: AuthenticationTokenType, security: AuthenticationTokenSecurity, identity: str, is_local: bool) AuthenticationToken

Creates an authentication token for a given identity. This token is given a unique identifier, which distinguishes it from other tokens.

Warning

The documentation is missing the description of the is_local parameter and it implications

Parameters:
  • type (AuthenticationTokenType) – A type of the authentication token.

  • security (AuthenticationTokenSecurity) – A level of security for the authentication token.

  • identity – A identity this token is bound to. In theory, no identity needs to be provided (such as access code to doors). However, not using it here may have unintended consequences.

  • is_local (bool) – No idea…

Returns:

An instance of authentication token.

abstractmethod register_authentication_token(provider: AuthenticationProvider, token: AuthenticationToken) bool

Registers an authentication token with the authentication provider. After that, anyone possessing the token can authenticate against the provider.

Parameters:
Returns:

Indication, whether registration was successful.

abstractmethod unregister_authentication_token(token_identity: str, provider: AuthenticationProvider) None

Unregisters authentication token from given provider based on given token identity.

Parameters:
  • token_identity (str) – An identity for token to be unregistered from provider.

  • provider (AuthenticationProvider) – An authentication provider to unregister token from.

Returns:

None

abstractmethod create_and_register_authentication_token(provider: AuthenticationProvider, identity: str) AuthenticationToken | None

A convenience function that combines the effects of create_authentication_token() and register_authentication_token().

Parameters:
  • provider (AuthenticationProvider) – An instance of the authentication provider.

  • identity (str) – A identity the created token is bound to. In theory, no identity needs to be provided (such as access code to doors). However, not using it here may have unintended consequences.

Returns:

An instance of authentication token that is already registered.

abstractmethod create_authorization(identity: str, access_level: AccessLevel, id: str, nodes: List[str] | None = None, services: List[str] | None = None) Authorization

Creates and authorization for a given identity. The authorization gives the identity access to selected nodes and services with a given access level.

Parameters:
  • identity (str) – An identity for whom the authorization is created.

  • access_level (AccessLevel) – An access level this authorization provides.

  • id (str) – A unique identifier of this authorization. TODO: I have no idea, what is the purpose.

  • nodes (Optional[List[str]]) – A list of node ids this authorization works on. This is intended for federated identities, which can are shared among different nodes and services. For local authentication providers, this parameters is of no use.

  • services (Optional[List[str]]) – A list of service ids this authorization works on. This is intended for federated identities, which can are shared among different nodes and services. For local authentication providers, this parameters is of no use.

Returns:

An authorization token.

abstractmethod create_access_scheme(id: str = '') AccessScheme

Creates an empty access scheme. The access scheme is a combination of authentication providers, which use a supplied authorizations. The access scheme provides means to describe multiple authentication schemes within one service or multi-factor authentication.

Parameters:

id (str) – A unique identification of the scheme within the simulation. If no value is provided, the system will generate a unique one.

Returns:

An empty access scheme.

abstractmethod add_provider_to_scheme(provider: AuthenticationProvider, scheme: AccessScheme) None

Adds an authentication provider to the access scheme.

Parameters:
Returns:

None

abstractmethod add_authorization_to_scheme(auth: Authorization, scheme: AccessScheme) None

Adds an authorization to the access scheme.

Parameters:
Returns:

None

abstractmethod remove_authorization_from_scheme(auth: Authorization, scheme: AccessScheme) None

Removes given authorization from given scheme by setting its access level to AccessLevel.NONE

Parameters:
Returns:

None

abstractmethod evaluate_token_for_service(service: Service, token: AuthenticationToken, node: Node, fallback_ip: IPAddress | None) Authorization | AuthenticationTarget | None

Evaluates if a token authenticates against a service. For single-factor authentications, this function returns either a working authorization token or none, depending on success. For multi-factor authentications, if it is the last factor, then it behaves as a single-factor authentication. If it is not, then it will either return a next authentication target in given scheme or none, depending on success.

TODO

Add the description of fallback_ip.

Parameters:
  • service (Service) – An instance of the service the evaluation is against.

  • token (AuthenticationToken) – An instance of the authentication token.

  • node (Node) – An instance of the node where the service resides on.

  • fallback_ip (Optional[IPAddress]) – No idea

Returns:

Either a working authorization, a next target, or just none if the authentication was not successful.

abstractmethod disable_authentication_token(provider: AuthenticationProvider, token: AuthenticationToken, time: int) None

Disables given token until given simulation time.

Parameters:
  • provider (AuthenticationProvider) – An authentication provider that contains token to be disabled.

  • token (AuthenticationToken) – An authentication token to be disabled.

  • time (int) – Simulation time

Returns:

None

abstractmethod enable_authentication_token(provider: AuthenticationProvider, token: AuthenticationToken) None

Enables given token.

Parameters:
Returns:

None

abstractmethod create_service_access(service: Service, identity: str, access_level: AccessLevel, tokens: List[AuthenticationToken] = None) List[AuthenticationToken] | None

Create a means to access the given passive service by registering authentication tokens under the given identity.

TODO: Currently works only for local providers.

Parameters:
  • service (PassiveService) – A passive service to grant access to.

  • identity (str) – An identity of the user which will gain the access. It must not be already present in one of the access schemes.

  • access_level (AccessLevel) – The level of access which will be created.

  • tokens (List[AuthenticationToken]) – Tokens to be optionally used instead of creating new ones.

Returns:

List of authentication tokens if successful, None otherwise.

abstractmethod modify_existing_access(service: Service, identity: str, access_level: AccessLevel) bool

Modify the access level of the given passive service. No new tokens are created.

TODO: Currently works only for local providers.

Parameters:
  • service (PassiveService) – A passive service to grant access to.

  • identity (str) – An identity of the user which access will be modified. It must be already present at one of the access scheme.

  • access_level (AccessLevel) – The level of access which will be modified.

Returns:

True if successful, False otherwise.

class cyst.api.environment.configuration.PhysicalConfiguration

Bases: ABC

Physical configuration enables manipulation with physical locations, their assets, and users within them.

abstractmethod create_physical_location(location_id: str | None) PhysicalLocation

Creates a new physical location. This location is empty an inaccessible.

Parameters:

location_id (str | None) – An identification of a location. If not provided, the id is autogenerated.

Returns:

A physical location handle.

abstractmethod get_physical_location(location_id: str) PhysicalLocation | None

Returns a physical location handle by given id.

Parameters:

location_id (str) – An identification of a location.

Returns:

A physical location handle if it exists with the given id, or None otherwise.

abstractmethod get_physical_locations() List[PhysicalLocation]

Returns all physical locations that are present in the current run.

Returns:

List of physical locations.

abstractmethod remove_physical_location(location_id: str) None

Removes a physical locations. Physical connections associated with it are removed as well. If there are assets and users at the physical location, there are moved to a limbo with other unassigned assets.

Parameters:

location_id (str) – An id of the location.

Returns:

None. Throws when attempting to remove nonexistent location.

abstractmethod create_physical_access(identity: str, time_from: datetime | None, time_to: datetime | None) PhysicalAccess

Creates a physical access specification. This specification can be used multiple times for different locations.

Parameters:
  • identity (str) – The identity of the user for which the access is being made. The identity is not checked for existence, therefore, access for non-existent users can be made.

  • time_from (datetime | None) – A specification of a time of a day (the date part is ignored) from which the access is granted. If no time is provided, then 00:00:00 is assumed.

  • time_to (datetime | None) – A specification of a time of a day (the date part is ignored) to which the access is granted. If no time is provided, then 23:59:59 is assumed.

Returns:

A physical access handle.

abstractmethod add_physical_access(location_id: str, access: PhysicalAccess) None

Adds physical access to a given location. Accesses may overlap, asi it does not matter, because they all represent an ALLOW rule.

Parameters:
  • location_id (str) – The ID of a location where to add the access. Throws if nonexistent.

  • access (PhysicalAccess) – A physical access handle.

Returns:

None

abstractmethod get_physical_accesses(location_id: str) List[PhysicalAccess]

Lists all accesses that are in effect at a given location.

Parameters:

location_id (str) – The ID of a location to list accesses from. Throws if nonexistent.

Returns:

Physical access handles for the given location.

abstractmethod remove_physical_access(location_id: str, access: PhysicalAccess) None

Removes physical access from the given location.

Parameters:
  • location_id (str) – The ID of a location to remove accesses from. Throws if nonexistent.

  • access (PhysicalAccess) – A physical access handle.

Returns:

None

abstractmethod add_physical_connection(origin: str, destination: str, travel_time: Duration) None

Creates a new physical connection between two locations. If such connection already exists, a new connection is not created.

Parameters:
  • origin (str) – An id of a location which is the first endpoint of the connection. Throws if nonexistent.

  • destination (str) – An id of a location which is the second endpoint of the connection. Throws if nonexistent.

  • travel_time (Duration) – A duration needed to traverse the connection.

Returns:

None

abstractmethod remove_physical_connection(origin: str, destination: str) None

Removes a physical connection between two locations. If the connection does not exist, this is no-op.

Parameters:
  • origin (str) – An id of a location which is the first endpoint of the connection. Throws if nonexistent.

  • destination (str) – An id of a location which is the second endpoint of the connection. Throws if nonexistent.

Returns:

None

abstractmethod get_physical_connections(origin: str, destination: str | None) List[PhysicalConnection]

Get a list of physical connections between locations.

Parameters:
  • origin (str) – An id of a location which is one of the endpoints of the physical connection. Throws if nonexistent.

  • destination (str) – An id of a location which is another one of the endpoints of the physical connections. If no destination is specified, then this function returns all physical connections from/to an origin. Throws is nonexistent.

Returns:

A list of physical connections.

abstractmethod place_asset(location_id: str, asset: str) None

Add an asset to a location. For this purpose, users are treated as assets, much like nodes and others. This is a deus ex machina function. The asset appears in the location instantaneously. The function only controls if the asset is already present elsewhere. In such case the asset is removed from the other location and placed in this one. If the asset is already assigned to the given location, nothing happens.

Parameters:
  • location_id (str) – An ID of a location where to place the asset. Throws if nonexistent.

  • asset (str) – An ID of the asset to add to a location.

Returns:

None

abstractmethod remove_asset(location_id: str, asset: str) None

Removes an asset from a location. For this purpose, users are treated as assets, much like nodes and others. This is a deus ex machina function. The asset is removed from the location instantaneously and is placed in a limbo.

Parameters:
  • location_id (str) – An ID of a location from which to remove the asset. Throws if nonexistent.

  • asset (str) – An ID of the asset to remove from a location.

Returns:

None

abstractmethod move_asset(origin: str, destination: str, asset: str) Tuple[bool, str, str]

Moves an asset between two locations. For this purpose, users are treated as assets, much like nodes and others. Before the move, it is checked, whether there is a physical connection between the locations. If so, then the asset traverses the connection for given duration, and if it is the User, it is checked, whether they have access rights.

All assets are traversing the connection for the same time, so for example, if you want to mimic a moving of a piece of hardware between two locations, do not forget to add reasonable delays before and after the move to simulate packing and unpacking.

Parameters:
  • origin (str) – An id of a location which is the first endpoint of the connection. Throws if nonexistent.

  • destination (str) – An id of a location which is the second endpoint of the connection. Throws if nonexistent.

  • asset (str) – An ID of the asset to move between locations.

Returns:

A tuple representing (<was the move successful?>, <what is the location of asset after the move>, <a problem description if there was any>)

abstractmethod get_assets(location_id: str) List[str]

Get the list of assets present at the specified location.

Parameters:

location_id (str) – A name of a location from which to get the assets. Throws if nonexistent.

Returns:

A list of assets.

abstractmethod get_location(asset: str) str

Get the location of a given asset.

Parameters:

asset (str) – The ID of the asset for which you want the location.

Returns:

The id of a location, or an empty string if the asset is in limbo.

class cyst.api.environment.configuration.RuntimeConfiguration(data_backend: str = 'memory', data_backend_params: ~typing.Dict[str, str] = <factory>, data_batch_storage: bool = False, run_id: str = <factory>, run_id_log_suffix: bool = False, config_id: str = '', config_filename: str = '', max_running_time: float = 0.0, max_action_count: int = 0, other_params: ~typing.Dict[str, str] = <factory>)

Bases: object

Runtime configuration represents parametrization of running core instances. This configuration can come from multiple sources, e.g., environment variables, files, or command line parameters.

Parameters:
  • data_backend (str) – Specification of the backend for the data store. CYST by default provides two data store backends - ‘memory’ and ‘sqlite’.

  • data_backend_params (Dict[str, str]) – Parametrization of the data backend. Once something else than parameter-less memory backend works, it will be documented here.

  • data_batch_storage (bool) – If set to true, data to be stored in the backend are first stored into the memory and only then transferred to the chosen backend.

  • run_id (str) – The current run id, regardless of it was autogenerated, or provided by a user.

  • run_id_log_suffix (bool) – If set to true, run_id will be a part of log file names.

  • config_id (str) – An identifier of a configuration for the current run, if the configuration is retrieved from a database.

  • config_filename (str) – A path to a file where a configuration for the current run is present.

  • max_running_time (float) – An upper limit on an execution time of a run. A platform time is considered, not the real time. The run is not guaranteed to finish at exactly the specified time, rather it will gracefully finish if the running time is exceeded.

  • max_action_count (int) – An upper limit on the number of executed actions by any actor. When this count is reached, the run terminates. Only the top-level actions (i.e., not actions executed within composite actions) are counted and the run is not guaranteed to finish at exactly the specified action count. Rather, it will gracefully finish if the action count is exceeded.

  • other_params (Dict[str, str]) – A dictionary of parameters that are not directly consumed by the environment but are passed to the other components, agents, etc.

cyst.api.environment.control

class cyst.api.environment.control.EnvironmentState(*values)

Bases: Enum

State of the environment

State transition diagram:

                      reset()                 terminate()
                     ┌────────── TERMINATED ◄───────────┐
                     │               ▲                  │
                     │    terminate()│                  │
                     ▼               │    ──pause()──►  │
CREATED ──init()─► INIT ──run()─► RUNNING             PAUSED
                     ▲               │    ◄──run()───
                     │               │
                     │               ▼
                     └────────── FINISHED
                      reset()
Possible values:
CREATED:

The environment was jus created.

INIT:

The environment was initialized and is ready to run.

RUNNING:

The environment is currently running a simulation.

PAUSED:

The environment is paused. If unpaused, the messages on stack will be sent and simulation resumes.

FINISHED:

The environment finished a simulation run. This means either no more messages on stack, or running past the specified time.

TERMINATED:

The environment was forcefully stopped. It cannot be resumed, but its internal state can be investigated.

class cyst.api.environment.control.EnvironmentControl

Bases: ABC

EnvironmentControl provides mechanisms to control the execution of actions within the simulation environment.

Availability:
Available:

creator

Hidden:

agents, models

abstract property state: EnvironmentState

Provides the current state of the environment.

Returns:

State of the environment

Return type:

EnvironmentState

abstractmethod init() Tuple[bool, EnvironmentState]

Initializes the environment for the first time. The environment must be in the CREATED state. The following invocations do nothing and silently return true.

Returns:

A tuple indicating, whether the operation was successful and which state the environment ended in.

abstractmethod commit() None

Stores the information of the currently executed run into the data store. This can only be executed from the FINISHED or TERMINATED state.

Returns:

None

abstractmethod reset(run_id: str = '238106fa-be4f-40e0-ae74-ee1c62608d26') Tuple[bool, EnvironmentState]

Resets the environment for another run. Only a previously FINISHED or TERMINATED run can be reset.

Parameters:

run_id (str) – The unique id of the current run. If a non-unique id is selected, it may produced unwanted results when saving the data to a data store.

Returns:

A tuple indicating, whether the operation was successful and which state the environment ended in.

abstractmethod run() Tuple[bool, EnvironmentState]

Starts or resumes the message processing in the current run. If the environment is in the INIT state, it activates the active services. If it is in INIT or PAUSED state, it begins message processing and transitions into the RUNNING state.

Returns:

A tuple indicating, whether the operation was successful and which state the environment ended in.

abstractmethod pause() Tuple[bool, EnvironmentState]

Invokes an explicit transition into the PAUSED state and temporarily halts the message processing. Can only be applied in the running state.

Returns:

A tuple indicating, whether the operation was successful and which state the environment ended in.

abstractmethod terminate() Tuple[bool, EnvironmentState]

Halts the message processing and transitions the environment into the TERMINATED state. From this state the environment cannot be re-run.

Returns:

A tuple indicating, whether the operation was successful and which state the environment ended in.

abstractmethod add_pause_on_request(id: str) None

Adds an explicit interrupt to message processing, whenever a service sends a request. Transitions the environment into the PAUSED state. Used mainly in tests to break from the run().

Parameters:

id – A fully qualified id of a service, i.e., also containing the node id.

Returns:

None

abstractmethod remove_pause_on_request(id: str) None

Removes an explicit interrupt to message processing, whenever a service sends a request.

Parameters:

id – A fully qualified id of a service, i.e., also containing the node id.

Returns:

None

abstractmethod add_pause_on_response(id: str) None

Adds an explicit interrupt to message processing, whenever a service receives a response. Transitions the environment into the PAUSED state. Used mainly in tests to break from the run().

Parameters:

id – A fully qualified id of a service, i.e., also containing the node id.

Returns:

None

abstractmethod remove_pause_on_response(id: str) None

Removes an explicit interrupt to message processing, whenever a service sends a request.

Parameters:

id – A fully qualified id of a service, i.e., also containing the node id.

Returns:

None

abstractmethod snapshot_save() str

Returns a complete state of the environment, from which it can be restored.

Returns:

String representation of the environment

abstractmethod snapshot_load(state: str) None

Attempts to restore the environment into the state described by the state string.

Parameters:

state (str) – State representation obtained by snapshot_save function. Currently, the snapshots are not guaranteed to work across versions.

Returns:

None

abstractmethod transaction_start() Tuple[int, int, str]

Initiates a new transaction, which sets a point in time to which the environment can be reverted. The transaction has to begin as a first thing in a given time slot, i.e., if there was already a message processed in the time slot, the transaction will be set to start in the next time slot.

Multiple calls to transaction_start() at the same time slot start only one transaction.

Transactions can be interleaved. In case of a rollback, the state always returns to the beginning of transaction that was rolled back.

Transactions do not necessarily modify the state of active services, it is up to their authors, if they want to preserve, e.g., learned behavior across transactions.

Returns:

A tuple containing transaction_id of started transaction, the time when the transaction starts, and additional information, if there is any.

abstractmethod transaction_commit(transaction_id: int) Tuple[bool, str]

Finalizes a transaction. Such transaction is removed from the system and cannot be rolled back.

Any transaction can be committed by anyone. There is no validation of the caller.

Parameters:

transaction_id (int) – The id of a transaction to commit.

Returns:

A tuple indicating whether the transaction was successfully committed and providing additional information if present.

abstractmethod transaction_rollback(transaction_id: int) Tuple[bool, str]

Returns the system into the state at the beginning of a transaction. After the transaction is rolled back, it is removed from the system.

Any transaction can be rolled back by anyone. There is no validation of a caller.

Parameters:

transaction_id (int) – The ID of the transaction to roll back.

Returns:

A tuple indicating whether a rollback was successful and providing additional information in present.

cyst.api.environment.external

This module enables accessing of resources outside the CYST environment in a manner that is conformant to the requirements of the internal processing and simulated/real time.

class cyst.api.environment.external.ResourcePersistence(*values)

Bases: Enum

Resources can be created in one of the two modes - transient and persistent. In the transient mode, they are opened and closed for each operation on them. In the persistent mode, they get opened on the first operation and must be closed via the release_resource() function.

Possible values:
TRANSIENT:

Open/close on each operation.

PERSISTENT:

Stay opened after the first operation.

class cyst.api.environment.external.Resource

Bases: ABC

Resource interface represents an interface that is user-facing. A user does not interact with the resource directly, but is instead using the ExternalResources interface. Therefore, this interface provides only some minimal information about the resource.

abstract property path: str

The URL of the resource. Technically, many different resources can share the same URL and be differentiated just by the parameters that are provided with particular operations on the ExternalResources interface. Therefore, this property is only informative.

abstract property persistence: ResourcePersistence

The persistence setting of the resource.

class cyst.api.environment.external.ResourceImpl

Bases: Resource, ABC

This interface represents an implementation of a resource that is used by the ExternalResource interface. Any new or custom resources that are added to the system must inherit from both Resource and ResourceImpl.

abstractmethod init(path: ParseResult, params: dict[str, str] | None = None, persistence: ResourcePersistence = ResourcePersistence.TRANSIENT) bool

This function is called when the resource is initialized after being created.

Parameters:
  • path (ParseResult) – The result of urllib.parse() call on the URL. The scheme is always guaranteed to be the same as the one used when this resource was registered.

  • params (Optional[dict[str, str]]) – Arbitrary parameters that may be required for resource initialization. If the resource is created implicitly via the ExternalResources interface, these parameters are shared with the call to send()/receive().

  • persistence (ResourcePersistence) – Persistence of a result. Implicitly created resources are always transient.

Returns:

True if the resource was successfully created and False otherwise.

abstractmethod open() None

This function is called prior to send/receive functions. It should prepare the resource for interactions.

Returns:

None

abstractmethod close() None

This function closes the resource. Any interaction after closing should fail. Close is either called immediately after send()/receive() operation for transient resources, or called when the resource is released for persistent ones.

Returns:

None

abstractmethod async send(data: str, params: dict[str, str] | None = None) int

This function sends the data to the resource, e.g., writes to the socket, writes to a file, or inserts into a database. Due to its async nature, you should utilize the async I/O operations, as the sync ones can disturb the execution of the rest of the processing.

Parameters:
  • data (str) – The data that should be written.

  • params (Optional[dict[str, str]]) – Arbitrary parameters that modify the send operation. If the resource was implicitly created, these parameters may contain ones that are related to resource creation.

Returns:

The number of characters that were actually written, even though the expectation here is that this function sends all the data or fails.

abstractmethod async receive(params: dict[str, str] | None = None) str | None

This function read the data from the resource. Due to its async nature, you should utilize the async I/O operations, as the sync ones can disturb the execution of the rest of the processing.

Parameters:

params (Optional[dict[str, str]]) – Arbitrary parameters that modify the receive operation. If the resource was implicitly created, these parameters may contain ones that are related to resource creation.

Returns:

The data received from the resource or None if nothing was available or expected, e.g., HEAD HTTP request.

class cyst.api.environment.external.ExternalResources

Bases: ABC

External resources represent any resources that are not part of the simulation/emulation runs, such as files, REST APIs, databases, etc. To maintain a sensible and consistent relation to underlying platforms and their time, all such resources must be handled through this interface.

Warning

Due to technical reasons related to dependence on an underlying platform, ExternalResources can be used only in the init/post-init state, including the attempts to register custom resources. That is, resources cannot be managed from the __init__ methods of behavioral models, platforms, or services.

abstractmethod register_resource(scheme: str, resource: Type | ResourceImpl) bool

Register a given resource for given scheme.

Parameters:
  • scheme (str) – URL scheme, i.e., the part before ://.

  • resource (Union[Type, ResourceImpl]) – The resource can either be provided as a type that implements the Resource and ResourceImpl interfaces, or it can be an object instance. If a type is provided then a new instance of the resource is created for each call of create_resource(), including the implicit call in sending and fetching function. If a resource instance is provided then this resource acts as a singleton and multiple interleaved open() and close() calls can happen and the resource must be prepared to handle it gracefully.

Returns:

True if the resource was successfully registered. False otherwise.

abstractmethod create_resource(path: str, params: dict[str, str] | None = None, persistence: ResourcePersistence = ResourcePersistence.TRANSIENT) Resource

Creates an instance of a resource with a given path.

Parameters:
  • path (str) – The URL of the resource. Full URL with scheme, e.g., file:// must be supplied to let system choose the correct one.

  • params (Optional[dict[str, str]]) – Arbitrary set of parameters that will be passed to the resource’s init function. If the resource is a singleton (cf. register_resource()) then these parameters are ignored.

  • persistence (ResourcePersistence) – The persistence of a resource. The create_resource() function is usually called implicitly from the other functions, which set the persistence to transient. However, if you do not want the resource to be created and destroyed after each call, set the persistence to ResourcePersistence.PERSISTENT.

Returns:

An instance of a resource. Otherwise, an exception is thrown.

abstractmethod release_resource(resource: Resource) None

Releases a persistent resource. For a transient resource, this is a no-operation.

Parameters:

resource (Resource) – An instance of a resource to close.

Returns:

Nothing. This call always succeeds.

abstractmethod async send_async(resource: str | Resource, data: str, params: dict[str, str] | None = None, timeout: float = 0.0) int

Asynchronously send data to a resource. This function is intended to be used by behavioral models and platforms. The agents currently must use the synchronous option.

Parameters:
  • resource (Union[str, Resource]) – Either an instance of a resource, or its URL. When the URL is used, the resource is automatically created and destroyed within the call.

  • data (str) – Any data will be written to the resource. Technically, this can be empty and the contents can depend on the params.

  • params (Optional[dict[str, str]]) – Parameters for the sending operation. Such as choosing the write or append mode for the file resource.

  • timeout (float) – A maximum time given for the operation to finish. The semantic differs for simulated and emulated environments. For simulated ones, the timeout represents the actual length in virtual time units, whereas for emulated ones, the timeout is the maximum time to finish. That is, simulated send will always take timeout number of time units, and emulated will take up-to timeout time units.

Returns:

The number of bytes written. However, the sending operation is expected to write all of its contents. In case of some error an exception should be thrown.

abstractmethod send(resource: str | Resource, data: str, params: dict[str, str] | None = None, timeout: float = 0.0, callback_service: ActiveService | None = None) None

Synchronously send data to a resource. This function is expected to be used by agents, but it can be with a bit of over-engineering used by behavioral models and platforms as well. The main difference is that the results of the send operation are send inside a MessageType.RESOURCE message.

Parameters:
  • resource (Union[str, Resource]) – Either an instance of a resource, or its URL. When the URL is used, the resource is automatically created and destroyed within the call.

  • data (str) – Any data will be written to the resource. Technically, this can be empty and the contents can depend on the params.

  • params (Optional[dict[str, str]]) – Parameters for the sending operation. Such as choosing the write or append mode for the file resource.

  • timeout (float) – A maximum time given for the operation to finish. The semantic differs for simulated and emulated environments. For simulated ones, the timeout represents the actual length in virtual time units, whereas for emulated ones, the timeout is the maximum time to finish. That is, simulated send will always take timeout number of time units, and emulated will take up-to timeout time units.

  • callback_service (Optional[ActiveService] = None) – A reference to an active service that will receive the result of the call within a resource message.

Returns:

None. The sending operation is always expected to write all of its contents. In case of some error an exception is thrown.

abstractmethod async fetch_async(resource: str | Resource, params: dict[str, str] | None = None, timeout: float = 0.0) str | None

Asynchronously reads data from a resource. This function is intended to be used by behavioral models and platforms. The agents currently must use the synchronous option.

Parameters:
  • resource (Union[str, Resource]) – Either an instance of a resource, or its URL. When the URL is used, the resource is automatically created and destroyed within the call.

  • params (Optional[dict[str, str]]) – Parameters for the receiving operation.

  • timeout (float) – A maximum time given for the operation to finish. The semantic differs for simulated and emulated environments. For simulated ones, the timeout represents the actual length in virtual time units, whereas for emulated ones, the timeout is the maximum time to finish. That is, simulated send will always take timeout number of time units, and emulated will take up-to timeout time units.

Returns:

The data read from the resource. It may return none and still be correct, such as the HEAD HTML request.

abstractmethod fetch(resource: str | Resource, params: dict[str, str] | None = None, timeout: float = 0.0, callback_service: ActiveService | None = None) None

Synchronously reads data from a resource. This function is expected to be used by agents, but it can be with a bit of over-engineering used by behavioral models and platforms as well. The main difference is that the results of the fetch operation are send inside a MessageType.RESOURCE message.

Parameters:
  • resource (Union[str, Resource]) – Either an instance of a resource, or its URL. When the URL is used, the resource is automatically created and destroyed within the call.

  • params (Optional[dict[str, str]]) – Parameters for the receiving operation.

  • timeout (float) – A maximum time given for the operation to finish. The semantic differs for simulated and emulated environments. For simulated ones, the timeout represents the actual length in virtual time units, whereas for emulated ones, the timeout is the maximum time to finish. That is, simulated send will always take timeout number of time units, and emulated will take up-to timeout time units.

  • callback_service (Optional[ActiveService] = None) – A reference to an active service that will receive the result of the call within a resource message.

Returns:

The data read from the resource. It may return none and still be correct, such as the HEAD HTML request.

cyst.api.environment.infrastructure

class cyst.api.environment.infrastructure.EnvironmentInfrastructure

Bases: ABC

This interface provides access to environment resources that are aimed at behavioral models and to applications utilizing CYST.

abstract property statistics: Statistics

Statistics track basic information about the simulation runs.

Return type:

Statistics

abstract property data_store: DataStore

Data store provides access to storing run-related data for later analysis.

Return type:

DataStore

abstract property service_store: ServiceStore

Provides access to creation and querying of active services.

Return type:

ServiceStore

abstract property runtime_configuration: RuntimeConfiguration

Provides access to parameters that were set either through a command line or through environmental variables.

Return type:

RuntimeConfiguration

cyst.api.environment.interpreter

class cyst.api.environment.interpreter.ActionInterpreter(*args, **kwargs)

Bases: ABC

An action interpreter provides a semantic to the actions. It analyzes the incoming requests and then make appropriate changes in the environment and prepares responses that are sent back to the requests’ origin.

Deprecated since version 0.6.0: Replaced by behavioral models.

abstractmethod evaluate(message: Request, node: Node) Tuple[int, Response]

Evaluates the request.

Parameters:
  • message (Request) – The request containing an action to be done.

  • node (Node) – An instance of the node, where the message ended.

Returns:

A tuple indicating the length of processing in the simulated time units and the Response that should be sent to the origin.

class cyst.api.environment.interpreter.ActionInterpreterDescription(*args, **kwargs)

Bases: object

A definition of an action interpreter.

Parameters:
  • namespace (str) – A namespace in which all interpreted actions belong to. An arbitrary namespace nesting is supported through the dot notation (e.g., “a.b.c” namespaces and actions of the form “a.b.c.action_n”).

  • description (str) – A textual description of the action interpreter. The description should introduce the behavioral model that the interpreter implements, so that the users can decide whether to use it or not.

  • creation_fn (Callable[[EnvironmentConfiguration, EnvironmentResources, EnvironmentMessaging], ActionInterpreter]) – A factory function that can create the interpreter.

Deprecated since version 0.6.0: Replaced by behavioral models.

cyst.api.environment.message

class cyst.api.environment.message.MessageType(*values)

Bases: Enum

The type of the message.

Possible values:

TIMEOUT:

The message is an information about expiration of a timeout.

REQUEST:

The message is a request.

RESPONSE:

The message is a response.

RESOURCE:

The message carries a resource.

SIGNAL:

The message is a signaling between the environment and components

class cyst.api.environment.message.StatusOrigin(*values)

Bases: Enum

Indicates the origin of the status of the response.

Possible values:

NETWORK:

The original request was processed and evaluated at the network or router level.

NODE:

The original request was processed and evaluated at the node level, without reaching a specific service.

SERVICE:

The original request was processed and evaluated at the service level.

RESOURCE:

The original request was for the resources.

SYSTEM:

The original request couldn’t be evaluated in the environment context. This indicates a system error.

class cyst.api.environment.message.StatusValue(*values)

Bases: Enum

Indicates the general result of the response.

Possible values:

SUCCESS:

The action intended in the original request was successfully carried out.

FAILURE:

The action intended in the original request was not successful due to wrong parameter combination or values.

ERROR:

The action intended in the original request was not successful because of missing parameters or because the action could not be evaluated.

PARTIAL:

The action intended in the original request was partially successful and another response should be expected to arrive soon.

class cyst.api.environment.message.StatusDetail(*args, **kwargs)

Bases: Flags

Status detail provides another introspection mechanism to active services into the nature of failures and errors. Status detail follows unified naming convention WHAT_WHY, where WHY is one of the following:

  • NOT_PROVIDED: WHAT was not passed as a parameter, even though it is required

  • NOT_EXISTING: WHAT does not exist within the context of current simulation run (e.g., service name, user name, etc.)

  • NOT_APPLICABLE: WHAT cannot be used (e.g., wrong authorization, wrong exploit parameters, etc.)

  • NOT_SUPPORTED: WHAT exists as a valid concept, but the target does not support it (e.g., attempting to open a session to a service that does not support it)

  • NEXT: WHAT was a correct step towards success, but another WHAT is required

General:
UNKNOWN:

There is no additional detail to be provided.

NODE.FAILURE:
PRIVILEGES_NOT_APPLICABLE:

A wrong authentication/authorization used.

NODE.ERROR:
SERVICE_NOT_PROVIDED:

No service was specified.

SERVICE_NOT_EXISTING:

A service does not exist at the node.

SESSION_NOT_PROVIDED:

A session was required but not provided.

SESSION_NOT_APPLICABLE:

A wrong session was provided.

SERVICE.FAILURE:
SESSION_CREATION_NOT_SUPPORTED:

A session cannot be created with this service.

EXPLOIT_NOT_PROVIDED:

An action required an exploit, but it was not provided.

EXPLOIT_NOT_APPLICABLE:

An exploit could not be used in combination with an action to affect the service.

EXPLOIT_CATEGORY_NOT_APPLICABLE:

Wrong exploit category was used (e.g., data manipulation exploit for privilege escalation)

EXPLOIT_LOCALITY_NOT_APPLICABLE:

Wrong exploit locality was used (e.g., local exploit for remote action)

EXPLOIT_PARAMETER_NOT_PROVIDED:

A required exploit parameter was missing.

EXPLOIT_PARAMETER_NOT_APPLICABLE:

A provided parameter in exploit could not be used to affect the service.

AUTHORIZATION_NOT_PROVIDED:

An authorization was missing when trying to access the service.

AUTHORIZATION_NOT_APPLICABLE:

A wrong authorization was used to access the service.

AUTHENTICATION_NOT_PROVIDED:

An authentication token was missing when trying to authenticate against the service.

AUTHENTICATION_NOT_APPLICABLE:

A wrong authentication token was used when trying to authenticate against the service.

AUTHENTICATION_NEXT:

A previous step in multi-factor authentication step was successful, another step must be undertaken.

SYSTEM.ERROR:
ACTION_NOT_EXISTING:

There is no interpreter able to interpret the given action.

class cyst.api.environment.message.Status(origin: ~cyst.api.environment.message.StatusOrigin, value: ~cyst.api.environment.message.StatusValue, detail: ~cyst.api.environment.message.StatusDetail = <StatusDetail.UNKNOWN bits=0x0001 data=UNDEFINED>)

Bases: object

Status is bound to responses, as it informs about the result of request processing.

Parameters:
class cyst.api.environment.message.Message

Bases: ABC

Message provides a mean to exchange information between elements of the simulation.

abstract property id: int

A numerical identifier of the message, which is unique across a simulation run. The request and response share the same id to enable correct pairing.

Return type:

int

abstract property type: MessageType

A type of the message.

Return type:

MessageType

abstract property src_ip: IPAddress | None

An IP address from which the message was sent. Messages not yet sent and timeouts do not have a source IP.

Return type:

Optional[IPAddress]

abstract property dst_ip: IPAddress | None

An IP address where the message was sent to. Timeouts do not have a destination IP.

Return type:

Optional[IPAddress]

abstract property src_service: str | None

A service that produced the message. In case of service-less origins, such as router processing, the service is empty.

Return type:

Optional[str]

abstract property dst_service: str

A service that is the ultimate target for the message.

Return type:

str

abstract property session: Session | None

A session associated with the message.

Return type:

Session

abstract property auth: Authorization | AuthenticationToken | AuthenticationTarget | None

An authentication/authorization associated with the message. In case of requests, it can either contain Authorizations or Authentication tokens provided by the origin. In case of responses, it can be the original Authorization provided by the origin, Authorization provided by the destination service as a result of an actions, or Authentication target if the request was a part of multi-factor authentication scheme message exchange.

Return type:

Optional[Union[Authorization, AuthenticationToken, AuthenticationTarget]]

abstract property ttl: int

Time-to-live of the message. After each pass through a router, this value gets decreased and the message gets discarded if it ever reaches 0. This mechanism exists to prevent endless resending of a message in case of bad network topology.

Return type:

int

abstract property metadata: Metadata

Metadata associated with the message. The metadata is supplied to the message by metadata providers (see cyst.api.environment.metadata_provider.MetadataProvider). The metadata mechanism is intended to provide at least some information for active services, who are not able to see the action portion of a message (at least that is the plan, currently they can).

Return type:

Metadata

abstractmethod set_metadata(metadata: Metadata) None

Sets a metadata to the message. This call overwrites the original value, so, if there is a multitude of metadata providers, it is advisable to use add_metadata().

Parameters:

metadata – A new value of the metadata.

abstract property platform_specific: Dict[str, Any]

Provides access to platform-specific parameters of a message. Their contents can be arbitrary and are intended for assisting with message handling in a non CYST-simulated environments.

Return type:

Dict[str, Any]

Added in version 0.6.0.

abstractmethod cast_to(type: Type[T]) T

This function enables typecasting of Message into one of the four derived types: Request, Response, Resource, and Timeout. While technically not necessary due to Python’s type system, it conveys intention and also makes a check whether the conversion can actually be done.

Parameters:

type (Type[TypeVar('T', bound=Union['Request', 'Response', 'Resource', 'Timeout'])]) – A type to cast the mesasge to.

Return type:

T

class cyst.api.environment.message.Request

Bases: Message, ABC

Request is a message specialization that carries an action.

abstract property action: Action

Gets an action associated with the request.

Return type:

Action

class cyst.api.environment.message.Response

Bases: Message, ABC

Response is a message specialization the carries the result of associated request.

abstract property action: Action

Gets an action associated with the original request.

Return type:

Action

Added in version 0.6.0.

abstract property status: Status

A result of processing the associated request.

Return type:

Status

abstract property content: Any | None

Any associated information that was included during the processing of the associated request.

Return type:

Optional[Any]

class cyst.api.environment.message.Timeout

Bases: Message, ABC

Timeout is a message specialization that is delivered when a timeout requested through the cyst.api.environment.clock.Clock interface.

abstract property start_time: float

A simulation time when the timeout was requested.

Return type:

int

abstract property duration: float

A duration of the timeout in simulated time units.

Return type:

int

abstract property parameter: Any

Any parameter that was included during the invocation of the timeout function.

Return type:

Any

class cyst.api.environment.message.Resource

Bases: Message, ABC

Resource is a message specialization that is delivered when an asynchronous external resource was requested.

abstract property path: str

A URL of the resource.

Rtype str:

abstract property status: Status

A result of processing the associated request.

Return type:

Status

abstract property data: str | None

Data retrieved from the external resource. In case the system failed to extract the data (due to timeout or error), the return is set to None.

Return type:

Optional[str]

class cyst.api.environment.message.ComponentState(*args, **kwargs)

Bases: Flags

Component state represents an abstract state of a particular component within simulation. The state is expressed as a flag, so, multiple states can be expressed at once.

States related to the component lifetime:
CREATED:

A component was just created.

INIT:

A component was successfully initialized.

RUNNING:

A component is running in a nominal fashion.

PAUSED:

An execution of component’s functions was paused.

STOPPED:

An execution of component’s functions was stopped.

FINISHED:

A component stopped its operation in a normal way.

TERMINATED:

A component stopped its operation through some external factor.

States related to action effects:
UNDER_ATTACK:

A component is a subject to hostile activity. Note that there is no state indicating that attack has ended.

UNDER_CONTROL:

A component was subject to hostile activity that resulted in access or control from the origin of activity.

RESTORED:

A component is no longer UNDER_CONTROL.

DETECTED:

A component was marked as doing malicious activities.

BLOCKED:

A component was blocked by a prevention system.

UNBLOCKED:

A component is no longer blocked.

States related to goal pursuit:
GOAL_REACHED:

A component reached its stated goal.

GOAL_UNREACHABLE:

A component is unable to reach its goal.

States related to the iteration or repeated state occurrence:
FIRST_OCCURRENCE:

The state appeared for the first time (can also be the first occurRence after some major change)

REPEATED_OCCURRENCE:

The state apeeared multiple times (details can be provided by Signal.effect_parameters)

An example of a state evolution of a service under a successful attack:

ComponentState.UNDER_ATTACK ComponentState.UNDER_CONTROL ComponentState.TERMINATED

An example of a state evolution of a service that was attacked and cleaned afterward:

ComponentState.UNDER_ATTACK ComponentState.UNDER_CONTROL ComponentState.STOPPED ComponentState.RUNNING | ComponentState.RESTORED

An example of a attacker being detected and blocked (the state is reported by IPS, not the agent):

ComponentState.DETECTED ComponentState.BLOCKED | ComponentState.FIRST_OCCURRENCE … attacker continues with attack ComponentState.BLOCKED | ComponentState.REPEATED_OCCURRENCE … attacker stops with an attack ComponentState.UNBLOCKED … attacker attacks again ComponentState.BLOCKED | ComponentState.FIRST_OCCURRENCE

An attacker signalling that it reached its goal and do not wish to continue the run:

ComponentState.FINISHED | ComponentState.GOAL_REACHED

class cyst.api.environment.message.Signal

Bases: Message, ABC

Signal is a message specialization that is used for communicating state changes between the environment and simulation components.

abstract property signal_origin: str

An identification of a source of the signal. This will usually be the ID of a component, or “__environment” if the signal is coming from within CYST.

Return type:

str

abstract property state: ComponentState

A new state that the component entered.

Return type:

ComponentState

abstract property effect_origin: str

An identification of a source of the state change that prompted the signal emission.

Return type:

str

abstract property effect_message: int | None

An ID of a message that caused the state change. In case of environment signals, there does not have to be any related message (such as the environment termination).

Return type:

Optional[int]

abstract property effect_description: str

A description of an effect used mainly for later analysis. For example “Maximum number of actions reached”, or “Successful execution of an action x:y”. This is not expected to be machine-processable .

Return type:

str

abstract property effect_parameters: Dict[str, Any]

Additional information related to the state change. For example, an agent may use this to signal its perceived reward, or some important training/execution parameters.

Return type:

Dict[str, Any]

cyst.api.environment.messaging

class cyst.api.environment.messaging.EnvironmentMessaging

Bases: ABC

This interface enables creating and sending of messages within simulation.

abstractmethod send_message(message: Message, delay: float = 0.0) None

Sends a message into the simulation for processing. The system will attempt to route the message to its destination. If there is a problem with delivery, an indication of it will be received in form of another message. This call always succeeds.

Parameters:
  • message (Message) – The message to be sent.

  • delay (float) – Simulation time delay between calling of this function and the actual dispatching of the message into the simulated infrastructure.

Returns:

None

abstractmethod create_request(dst_ip: str | IPAddress, dst_service: str = '', action: Action | None = None, session: Session | None = None, auth: Authorization | AuthenticationToken | None = None, original_request: Request | None = None) Request

Creates a message of type REQUEST. This function is a domain of active services.

Parameters:
  • dst_ip (Union[str, IPAddress]) – A destination IP address. If provided in the string form, it will attempt to convert it internally into the IPAddress.

  • dst_service (str) – The name of the service this message should be routed to.

  • action (Action) – An action that should be performed at the destination. Even though this function technically enables action-less message, it is an implementation detail and such message would be dropped at the first hop. TODO: Check it.

  • session (Session) – An optional session to use for a routing of this message.

  • auth (Union[Authorization, AuthenticationToken]) – Either authentication or authorization token to use for this message. If an authentication token is provided and the type of action is _not_ meta:authenticate, then the system will internally attempt to obtain an authorization by means of the meta:authenticate action and the proceed with the original action. More in the description of the authentication/authorization framework.

  • original_request (Request) – If provided, values from the original request are used to fill blanks in the new request. This is seldom needed in agent’s code and is more useful when creating behavioral models.

Returns:

A Request to be sent.

Changed in version 0.6.0: Added original request parameter to enable request copying

abstractmethod create_response(request: Request, status: Status, content: Any | None = None, session: Session | None = None, auth: Authorization | AuthenticationTarget | None = None, original_response: Response | None = None) Response

Creates a message of type RESPONSE. This response is always created from a request.

Parameters:
  • request (Request) – The request to which this response is created.

  • status (Status) – A status code of the response.

  • content (Optional[Any]) – A message body. Currently anything can be sent in the response, but it is expected that in future updates, the content will have its taxonomy and domains, so that automated reasoning can be fully applied to message processing.

  • session (Session) – Either the original session that is inherited from the request, or a new session that is created as a part of request processing. If the request arrived through a session, a session must be present with the response. Otherwise, the message would not be correctly routed.

  • auth (Optional[Union[Authorization, AuthenticationTarget]]) – By convention, this parameter should be the original request’s authorization token, unless the request resulted in a creation of a new authorization token, or unless the request-response is a part of a multi-factor authentication (in which case an Authenticationtarget is returned).

  • original_response (Response) – If provided, values from the original response are used to fill blanks in the new request. This is seldom needed in agent’s code and is more useful when creating behavioral models.

Returns:

A response to be sent.

Changed in version 0.6.0: Added original response parameter to enable response copying

abstractmethod create_signal(signal_origin: str, state: ComponentState, effect_origin: str, effect_message: int | None = None, effect_description: str = '', effect_parameters: Dict[str, Any] | None = None) Signal

Creates a message of type SIGNAL.

Parameters:
  • signal_origin (str) – An identification of a source of the signal. Usually an ID of your component.

  • state (ComponentState) – A new state that the component entered.

  • effect_origin (str) – An identification of a source of the state change that prompted the signal emission.

  • effect_message (Optional[int]) – An ID of a message that caused the state change.

  • effect_description (str) – A description of an effect used mainly for later analysis.

  • effect_parameters (Optional[Dict[str, Any]]) – Additional information related to the state change.

Added in version 0.6.0.

abstractmethod open_session(request: Request, reverse_direction: bool = False) Session

Opens a session to the source of the request. If the request already contains a session, it will append the session, unless the current node is an endpoint of that session. In that case, the same session is returned.

Parameters:
  • request (Request) – The request from which the session is established.

  • reverse_direction (bool) – Whether the direction of the shell is reversed or not.

Returns:

A session to the request source.

cyst.api.environment.metadata_provider

class cyst.api.environment.metadata_provider.MetadataProvider

Bases: ABC

Metadata providers supply messages with additional information that are meant to mimic observations that can be done in the real life. Two typical examples of metadata can be 1) flow information, 2) results of traffic analyses.

abstractmethod get_metadata(action: Action, message: Message) Metadata

This function should return metadata that correspond to the given message.

Parameters:
  • action (Action) – The action for which the metadata should be provided. Given that a message can carry multiple actions, a metadata provider must adhere to this parameter and not try to extract it from the message itself.

  • message (Message) – An instance of the message. The message should be used to provide additional information necessary to supply correct metadata, such as message type, message status, etc.

Returns:

A metadata associated with the message.

Changed in version 0.6.0: Metadata are collected for a whole message at the time of sending and are no longer solely dependent on Action.

class cyst.api.environment.metadata_provider.MetadataProviderDescription(namespace: str, description: str, creation_fn: Callable[[], MetadataProvider])

Bases: object

A description of a metadata provider which should be registered into the system.

Parameters:
  • namespace (str) – A namespace of actions that this provider works with.

  • description (str) – A short description of the provider.

  • creation_fn (Callable[[ActionStore, ActionConfiguration], MetadataProvider]) – A factory function that creates instances of the metadata provider.

cyst.api.environment.platform

class cyst.api.environment.platform.Platform

Bases: ABC

Platform interface provides an abstraction layer for different execution environments, e.g., simulation or emulation. When creating a new Environment a platform is instantiated and from then it takes care of message delivery and infrastructure configuration. As such, it is closely related to behavioral models, whose actions are custom tailored for a given platform.

abstractmethod init() bool

Initialize platform. After this call, the platform should correctly process all calls. The platform need not check whether the init was called, the core will execute it exactly once before calling anything else.

If the initialization was not successful, the platform should shutdown gracefully and release all resources. Error should be communicated via the logging interface.

Returns:

True if the initialization finished successfully. False otherwise.

abstractmethod terminate() bool

Terminate all platform operations. Within this call, all messages should be disposed of and the configured infrastructure should be torn down. This function will be called only once, so there is no need for checking.

If the termination was not successful, the platform should shutdown as much as possible and indicate problems via the logging interface.

Returns:

True if the termination finished successfully. False otherwise.

abstract property configuration: EnvironmentConfiguration

The configuration interface for the entire platform. In essence, platforms may implement up-to the extent of CYST simulation. But it is generally understood that the configuration options for, e.g., emulation platforms will be much more limited and will not enable all on-the-fly modifications.

Thus, platforms has to implement (and really implement because of usage of abstract base classes), the following:

  • NodeConfiguration: every method can end with exception

  • NetworkConfiguration: every method can end with exception

  • AccessConfiguration: every method can end with exception

  • ServiceConfiguration: get_service_interface() must be implemented, the rest can end with exception

  • ActionConfiguration: the one provided by the core can be used

  • ExploitConfiguration: the one provided by the core can be used

  • GeneralConfiguration:

    • get_configuration(), save_configuration(), load_configuration(), and get_configuration_by_id() should be proxied back to the core. get_object_by_id() must be implemented.

Returns:

An environment configuration interface.

abstractmethod configure(*config_item: ConfigItem) Platform

Configures the platform, according to provided configuration items. This function can be called repeatedly, however, each subsequent should replace the previous configuration. Therefore, a configuration must be done at once and every later change in the environment setup must be done through the cyst.api.environment.configuration.EnvironmentConfiguration interface.

Parameters:

config_item (ConfigItem) – One or more configuration items. The number of items can be arbitrary and it is not order-dependent.

Returns:

The configured platform.

abstract property messaging: EnvironmentMessaging

Provides access to messaging within the platform. Considering the variable nature of different platforms, they are expected to supply their own implementation of different messages ( cyst.api.environment.message.Message, cyst.api.environment.message.Request, cyst.api.environment.message.Response, cyst.api.environment.message.Timeout). They do not have to implement cyst.api.environment.message.Resource, as this is a domain of the core.

Returns:

An environment messaging interface.

abstractmethod async process(time_advance: float) bool

The main processing loop of the platform. Within this loop, the platform should process all messages that were enqueued through the cyst.api.environment.messaging.EnvironmentMessaging interface.

Parameters:

time_advance (float) – A hint to advance the time by the specified amount of time units. The platform is free to ignore the parameter in real-time based systems. In simulated systems, where the time is under the control of the platform the time should be advanced to prevent a deadlock.

Returns:

True if there was a message to process, False otherwise.

abstract property clock: Clock

Access to a platform time management.

Returns:

Clock interface.

class cyst.api.environment.platform.PlatformDescription(specification: PlatformSpecification, description: str, creation_fn: Callable[[PlatformInterface, GeneralConfiguration, EnvironmentResources, ActionConfiguration, ExploitConfiguration, PhysicalConfiguration, EnvironmentInfrastructure], Platform])

Bases: object

A description of a platform that is loaded as a module to CYST.

Parameters:

cyst.api.environment.platform_interface

class cyst.api.environment.platform_interface.PlatformInterface

Bases: ABC

An interface that enables platforms to communicate with the core to elicit effects.

abstractmethod execute_task(task: Message, service: Service | None = None, node: Node | None = None, delay: float = 0.0) Tuple[bool, float]

Instruct the core to execute included action with the appropriate behavioral model. The function signature follows the signature of cyst.api.logic.behavioral_model.action_effect().

Parameters:
  • task (Message) – The message containing the action. In virtually all cases this will be messages of type MessageType.REQUEST, however, there is nothing preventing a platform to call behavioral models for actions on responses.

  • service (Optional[Service]) – A service on which the action should be executed. The platform is free to ignore this parameter if its associated behavioral model does not require it.

  • node (Optional[Node]) – A node on which the action should be executed. The platform is free to ignore this parameter if its associated behavioral model does not require it.

  • delay (float) – A delay before the execution of the action.

Returns:

A tuple indicating whether the action was successfully executed and how long the execution took.

cyst.api.environment.platform_specification

class cyst.api.environment.platform_specification.PlatformType(*values)

Bases: Enum

Differentiates the modus operandi of the platform. As there is a blurry line between the simulation and emulation, we differentiate them in respect their control of time.

Possible values:

SIMULATED_TIME:

A platform fully controls execution time.

REAL_TIME:

A platform is operating in real-time.

class cyst.api.environment.platform_specification.PlatformSpecification(type: PlatformType, provider: str)

Bases: object

A specification of a platform. Each specification must be unique among the loaded modules.

Parameters:
  • type (PlatformType) – Type of execution of the platform.

  • provider (str) – A unique identifier of the platform. This string is used by the users to select the platform in the call to Environment’s create() method.

cyst.api.environment.policy

cyst.api.environment.resources

class cyst.api.environment.resources.EnvironmentResources

Bases: ABC

This interface provides access to resources that can be used by services within the simulation.

abstract property action_store: ActionStore

Through action store, services access actions that are available to them in the simulation.

Return type:

ActionStore

abstract property exploit_store: ExploitStore

Through exploit store, services access exploits that can be tied to actions.

Return type:

ExploitStore

abstract property clock: Clock

Clock provides a mean to track simulation and hybrid time.

Return type:

Clock

abstract property external: ExternalResources

External resources enable to interface simulation with data and services outside.

Return type:

ExternalResources

cyst.api.environment.stats

class cyst.api.environment.stats.Statistics

Bases: ABC

Statistics class tracks various statistical information about simulation runs.

abstract property run_id: str

Get the ID of the current run

Return type:

str

abstract property configuration_id: str

Get the ID of the configuration that is stored in the data store and that was used for the current run.

Return type:

str

abstract property start_time_real: float

Get the wall clock time when the current run started. The time is in the python time() format.

Return type:

float

abstract property end_time_real: float

Get the wall clock time when the current run was commited. The time is in the python time() format.

Return type:

float

abstract property end_time_virtual: int

Get the virtual time of the current run, i.e., the number of ticks. As the run always starts at 0, this function represents also the run duration.

Return type:

int

cyst.api.environment.stores

class cyst.api.environment.stores.ActionStore

Bases: ABC

Action store provides access to actions that are available to services.

abstractmethod get(id: str = '') Action | None

Returns an action with given ID. This function makes a copy of the object, which is present in the store. This is a preferred variant, because any parameters set on that action would propagate to the store.

Parameters:

id (str) – A unique ID of the action.

Returns:

An action, if there is one with such ID and for such execution environment.

abstractmethod get_ref(id: str = '') Action | None

Return an action with give ID. This function returns a reference to the object stored in the store and any parameter alterations will propagate to all subsequent queries for this action.

Parameters:

id (str) – A unique ID of the action.

Returns:

An action, if there is one with such ID and for such execution environment.

abstractmethod get_prefixed(prefix: str = '') List[Action]

Gets a list of actions, whose ID starts with a given string. This is usually done to get access to the entire namespace of a particular behavioral model.

The list will contain copies of actions present in the store. Getting multiple references in one call is not supported.

Parameters:

prefix (str) – The prefix all actions IDs must share.

Returns:

A list of actions with the same prefix.

abstractmethod add(action: ActionDescription) None

Adds a new action to the store. This function should be used in two cases:

  • Adding new action for a behavioral model. Such action must have a processing function mapped to the action ID.

  • Adding new action for intra-agent communication. There is no requirement on the action form, however an exception will be thrown, if this action is directed to a passive service, as the system will have no idea how to process it.

Parameters:

action (ActionDescription) – A description of the action to add.

Returns:

None

class cyst.api.environment.stores.ExploitStore

Bases: ABC

Exploit store provides access to exploits that can be used together with actions. Unlike the action store, runtime definition of exploits by services is not permitted. This must be done through the cyst.api.environment.configuration.ExploitConfiguration interface.

abstractmethod get_exploit(id: str = '', service: str = '', category: ExploitCategory = ExploitCategory.NONE) List[Exploit] | None

Gets an exploit, which satisfy all the parameters.

Parameters:
  • id (str) – An explicit ID of an exploit.

  • service (str) – An ID of a service the exploit can be used at.

  • category (ExploitCategory) – A category that the exploit should have. If the ExploitCategory.NONE is set, then the category is not considered when retrieving the exploits.

Returns:

A list of exploits satisfying the parameters.

abstractmethod evaluate_exploit(exploit: str | Exploit, message: Message, node: Node) Tuple[bool, str]

Evaluates, whether the provided exploit is applicable, given the message which carries the relevant action and a concrete node. TODO: This interface is cumbersome. While this is best fit for the data that interpreters receive, it is confusing at best.

Parameters:
  • exploit (Union[str, Exploit]) – The ID of the exploit or its instance.

  • message (Message) – An instance of the message which carried the exploit.

  • node (Node) – An instance of the node, where the exploit is being applied.

Returns:

(True, _) if exploit is applicable, (False, reason) otherwise.

class cyst.api.environment.stores.ServiceStore

Bases: ABC

Service store provides a unified interface for creating active services. Due to centrality of this concept to all CYST, regardless of the platform it uses, all services must be instantiated through this store.

Added in version 0.6.0.

abstractmethod create_active_service(type: str, owner: str, name: str, node: Node, service_access_level: AccessLevel = AccessLevel.LIMITED, configuration: Dict[str, Any] | None = None, id: str = '') ActiveService | None

Creates an active service instance.

Parameters:
  • type – The name of the active service, under which it is registered into the system.

  • owner – The identity of user, under whose identity this service should be running.

  • name – The name of the service, under which it is present at the node. Currently, it is used instead of ports to route messages. TODO: The name/id system is retarded and has to be changed.

  • node – The node at which the service should be instantiated.

  • service_access_level – The level of access the service has on the system.

  • id – System-wide unique ID of the service. Unless overridden, it will have the form of <node_id>.<service_name>.

  • configuration – A dictionary with arbitrary configuration items.

Returns:

The instantiated active service or None if there was an error.

abstractmethod get_active_service(id) ActiveService | None

Returns an already instantiated active service, given its full id (that is, not just a name).

Parameters:

id

Returns:

class cyst.api.environment.stores.DataStore

Bases: ABC

Added in version 0.6.0.

abstractmethod add_action(*action: ActionModel) None

Store information about a resolved action, i.e., after completing the request-response cycle.

Parameters:

action – An action(s) description

Returns:

None

abstractmethod add_message(*message: Message) None

Store information about a message. While in general, the message information is available only at the point of dispatching the message, platforms that have more control over the message passing process (e.g., simulation platform of CYST) may report the same message multiple times during the message passing. Additional information shall then be passed through the .platform_specific attributed, which gets erased before entering user-facing side of the code.

Parameters:

message – The message(s) to store

Returns:

None

abstractmethod add_statistics(statistics: Statistics) None

Store statistics related to one run.

Parameters:

statistics – The statistics to store.

Returns:

None

abstractmethod add_signal(*signal: Signal) None

Store a signal.

Parameters:

signal – The signal(s) to store.

Returns:

None

class cyst.api.environment.stores.DataStoreDescription(backend: str, description: str, creation_fn: Callable[[str, Dict[str, str]], DataStore])

Bases: object

Entry point for an implementation of a data store backend.

Parameters:
  • backend (str) – The name of the backend the data store uses. This name has to be unique within the system.

  • description (str) – A textual description of the data store backend.

  • creation_fn (Callable[[str, Dict[str, str]], DataStore]) – A factory function that can create the data store backend. Its parameters are the run_id and backend-specific key-value pairs.

Added in version 0.6.0.