toolsaf 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. toolsaf-0.2.0/Info.md +16 -0
  2. toolsaf-0.2.0/LICENSE +21 -0
  3. toolsaf-0.2.0/PKG-INFO +45 -0
  4. toolsaf-0.2.0/README.md +115 -0
  5. toolsaf-0.2.0/pyproject.toml +89 -0
  6. toolsaf-0.2.0/setup.cfg +4 -0
  7. toolsaf-0.2.0/tests/test_address.py +312 -0
  8. toolsaf-0.2.0/tests/test_arp.py +40 -0
  9. toolsaf-0.2.0/tests/test_builder.py +41 -0
  10. toolsaf-0.2.0/tests/test_critical_data.py +35 -0
  11. toolsaf-0.2.0/tests/test_dhcp.py +120 -0
  12. toolsaf-0.2.0/tests/test_dns.py +83 -0
  13. toolsaf-0.2.0/tests/test_event.py +110 -0
  14. toolsaf-0.2.0/tests/test_finder.py +43 -0
  15. toolsaf-0.2.0/tests/test_ignore_rules.py +109 -0
  16. toolsaf-0.2.0/tests/test_inspector.py +257 -0
  17. toolsaf-0.2.0/tests/test_listener.py +95 -0
  18. toolsaf-0.2.0/tests/test_matcher.py +107 -0
  19. toolsaf-0.2.0/tests/test_model.py +545 -0
  20. toolsaf-0.2.0/tests/test_model_new.py +42 -0
  21. toolsaf-0.2.0/tests/test_other_protocols.py +88 -0
  22. toolsaf-0.2.0/tests/test_property.py +17 -0
  23. toolsaf-0.2.0/tests/test_registry.py +141 -0
  24. toolsaf-0.2.0/tests/test_results.py +398 -0
  25. toolsaf-0.2.0/tests/test_selector.py +68 -0
  26. toolsaf-0.2.0/tests/test_software_backend.py +60 -0
  27. toolsaf-0.2.0/tests/test_source_matching.py +44 -0
  28. toolsaf-0.2.0/tests/test_uploader.py +111 -0
  29. toolsaf-0.2.0/toolsaf/__init__.py +0 -0
  30. toolsaf-0.2.0/toolsaf/adapters/__init__.py +0 -0
  31. toolsaf-0.2.0/toolsaf/adapters/android_manifest_scan.py +89 -0
  32. toolsaf-0.2.0/toolsaf/adapters/batch_import.py +297 -0
  33. toolsaf-0.2.0/toolsaf/adapters/censys_scan.py +78 -0
  34. toolsaf-0.2.0/toolsaf/adapters/certmitm_reader.py +71 -0
  35. toolsaf-0.2.0/toolsaf/adapters/data/android_permissions.json +367 -0
  36. toolsaf-0.2.0/toolsaf/adapters/github_releases.py +57 -0
  37. toolsaf-0.2.0/toolsaf/adapters/har_scan.py +117 -0
  38. toolsaf-0.2.0/toolsaf/adapters/nmap_scan.py +129 -0
  39. toolsaf-0.2.0/toolsaf/adapters/pcap_reader.py +251 -0
  40. toolsaf-0.2.0/toolsaf/adapters/ping_command.py +59 -0
  41. toolsaf-0.2.0/toolsaf/adapters/setup_reader.py +44 -0
  42. toolsaf-0.2.0/toolsaf/adapters/shodan_scan.py +231 -0
  43. toolsaf-0.2.0/toolsaf/adapters/spdx_reader.py +80 -0
  44. toolsaf-0.2.0/toolsaf/adapters/ssh_audit_scan.py +74 -0
  45. toolsaf-0.2.0/toolsaf/adapters/testsslsh_scan.py +73 -0
  46. toolsaf-0.2.0/toolsaf/adapters/tool_finder.py +104 -0
  47. toolsaf-0.2.0/toolsaf/adapters/tools.py +237 -0
  48. toolsaf-0.2.0/toolsaf/adapters/tshark_reader.py +71 -0
  49. toolsaf-0.2.0/toolsaf/adapters/vulnerability_reader.py +46 -0
  50. toolsaf-0.2.0/toolsaf/adapters/web_checker.py +82 -0
  51. toolsaf-0.2.0/toolsaf/adapters/zed_reader.py +64 -0
  52. toolsaf-0.2.0/toolsaf/builder_backend.py +1203 -0
  53. toolsaf-0.2.0/toolsaf/common/__init__.py +0 -0
  54. toolsaf-0.2.0/toolsaf/common/address.py +673 -0
  55. toolsaf-0.2.0/toolsaf/common/android.py +67 -0
  56. toolsaf-0.2.0/toolsaf/common/basics.py +37 -0
  57. toolsaf-0.2.0/toolsaf/common/entity.py +132 -0
  58. toolsaf-0.2.0/toolsaf/common/property.py +295 -0
  59. toolsaf-0.2.0/toolsaf/common/release_info.py +37 -0
  60. toolsaf-0.2.0/toolsaf/common/serializer/__init__.py +0 -0
  61. toolsaf-0.2.0/toolsaf/common/serializer/serializer.py +303 -0
  62. toolsaf-0.2.0/toolsaf/common/traffic.py +523 -0
  63. toolsaf-0.2.0/toolsaf/common/verdict.py +56 -0
  64. toolsaf-0.2.0/toolsaf/core/__init__.py +0 -0
  65. toolsaf-0.2.0/toolsaf/core/components.py +165 -0
  66. toolsaf-0.2.0/toolsaf/core/entity_database.py +89 -0
  67. toolsaf-0.2.0/toolsaf/core/entity_selector.py +32 -0
  68. toolsaf-0.2.0/toolsaf/core/event_interface.py +182 -0
  69. toolsaf-0.2.0/toolsaf/core/event_logger.py +254 -0
  70. toolsaf-0.2.0/toolsaf/core/ignore_rules.py +61 -0
  71. toolsaf-0.2.0/toolsaf/core/inspector.py +276 -0
  72. toolsaf-0.2.0/toolsaf/core/main_tools.py +106 -0
  73. toolsaf-0.2.0/toolsaf/core/matcher.py +620 -0
  74. toolsaf-0.2.0/toolsaf/core/model.py +920 -0
  75. toolsaf-0.2.0/toolsaf/core/online_resources.py +10 -0
  76. toolsaf-0.2.0/toolsaf/core/registry.py +105 -0
  77. toolsaf-0.2.0/toolsaf/core/result.py +354 -0
  78. toolsaf-0.2.0/toolsaf/core/selector.py +425 -0
  79. toolsaf-0.2.0/toolsaf/core/serializer/__init__.py +0 -0
  80. toolsaf-0.2.0/toolsaf/core/serializer/event_serializers.py +421 -0
  81. toolsaf-0.2.0/toolsaf/core/serializer/model_serializers.py +416 -0
  82. toolsaf-0.2.0/toolsaf/core/services.py +93 -0
  83. toolsaf-0.2.0/toolsaf/core/uploader.py +133 -0
  84. toolsaf-0.2.0/toolsaf/diagram_visualizer/backend.png +0 -0
  85. toolsaf-0.2.0/toolsaf/diagram_visualizer/browser.png +0 -0
  86. toolsaf-0.2.0/toolsaf/diagram_visualizer/device.png +0 -0
  87. toolsaf-0.2.0/toolsaf/diagram_visualizer/device_with_multicast.png +0 -0
  88. toolsaf-0.2.0/toolsaf/diagram_visualizer/mobile.png +0 -0
  89. toolsaf-0.2.0/toolsaf/diagram_visualizer/mobile_with_multicast.png +0 -0
  90. toolsaf-0.2.0/toolsaf/diagram_visualizer/multicast.png +0 -0
  91. toolsaf-0.2.0/toolsaf/diagram_visualizer.py +189 -0
  92. toolsaf-0.2.0/toolsaf/main.py +495 -0
  93. toolsaf-0.2.0/toolsaf.egg-info/PKG-INFO +45 -0
  94. toolsaf-0.2.0/toolsaf.egg-info/SOURCES.txt +95 -0
  95. toolsaf-0.2.0/toolsaf.egg-info/dependency_links.txt +1 -0
  96. toolsaf-0.2.0/toolsaf.egg-info/requires.txt +14 -0
  97. toolsaf-0.2.0/toolsaf.egg-info/top_level.txt +1 -0
toolsaf-0.2.0/Info.md ADDED
@@ -0,0 +1,16 @@
1
+ # Toolsaf
2
+
3
+ Welcome to the **Toolsaf**, a tool-driven security assessment framework.
4
+ Toolsaf is for security assessment of _Internet of Things_ (IoT) and other systems using different security tools.
5
+ Security assessment process using Toolsaf has two main phases:
6
+
7
+ 1. Creation of a _security statement_ which describes security-related features of an IoT product or system.
8
+ 2. Verification of the security statement with security tools, thus confirming the security posture of the product or system.
9
+
10
+ Some of the applications of this approach could be:
11
+
12
+ 1. Security testing and regression testing of IoT systems.
13
+ 2. Communicating system security properites in a machine-readable and verifiable format.
14
+ 3. Documenting results of a system's reverse-engineering.
15
+
16
+ Toolsaf is an open-source project driven by [Test of Things](https://testofthings.com). More information about toolsaf can be found in [Toolsaf Github](https://github.com/testofthings/toolsaf).
toolsaf-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Oulu University Secure Programming Group
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
toolsaf-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,45 @@
1
+ Metadata-Version: 2.4
2
+ Name: toolsaf
3
+ Version: 0.2.0
4
+ Summary: Tool-Driven Security Assesment Framework
5
+ Author-email: Rauli Kaksonen <rauli.kaksonen@testofthings.com>
6
+ License: MIT License
7
+ Project-URL: Repository, https://github.com/testofthings/toolsaf.git
8
+ Project-URL: Documentation, https://github.com/testofthings/toolsaf/tree/main/documentation
9
+ Project-URL: Issues, https://github.com/testofthings/toolsaf/issues
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: packet-framing
16
+ Requires-Dist: censys
17
+ Requires-Dist: aiohttp
18
+ Requires-Dist: aiofiles
19
+ Requires-Dist: sqlalchemy
20
+ Requires-Dist: watchdog
21
+ Requires-Dist: colored
22
+ Requires-Dist: diagrams
23
+ Requires-Dist: shodan
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest; extra == "dev"
26
+ Requires-Dist: pylint; extra == "dev"
27
+ Requires-Dist: mypy; extra == "dev"
28
+ Dynamic: license-file
29
+
30
+ # Toolsaf
31
+
32
+ Welcome to the **Toolsaf**, a tool-driven security assessment framework.
33
+ Toolsaf is for security assessment of _Internet of Things_ (IoT) and other systems using different security tools.
34
+ Security assessment process using Toolsaf has two main phases:
35
+
36
+ 1. Creation of a _security statement_ which describes security-related features of an IoT product or system.
37
+ 2. Verification of the security statement with security tools, thus confirming the security posture of the product or system.
38
+
39
+ Some of the applications of this approach could be:
40
+
41
+ 1. Security testing and regression testing of IoT systems.
42
+ 2. Communicating system security properites in a machine-readable and verifiable format.
43
+ 3. Documenting results of a system's reverse-engineering.
44
+
45
+ Toolsaf is an open-source project driven by [Test of Things](https://testofthings.com). More information about toolsaf can be found in [Toolsaf Github](https://github.com/testofthings/toolsaf).
@@ -0,0 +1,115 @@
1
+ # Toolsaf
2
+
3
+ [Table of contents](documentation/README.md)
4
+
5
+ Welcome to the early version of **Toolsaf**, a tool-driven security assessment framework.
6
+ Toolsaf is for security assessment of _Internet of Things_ (IoT) and other systems using different security tools.
7
+ Security assessment process using Toolsaf has two main phases:
8
+
9
+ 1. Creation of a _security statement_ which describes security-related features of an IoT product or system.
10
+ 2. Verification of the security statement with security tools, thus confirming the security posture of the product or system.
11
+
12
+ Some of the applications of this approach could be:
13
+
14
+ 1. Security testing and regression testing of IoT systems.
15
+ 2. Communicating system security properites in a machine-readable and verifiable format.
16
+ 3. Documenting results of a system's reverse-engineering.
17
+
18
+ Toolsaf is an open-source project driven by [Test of Things](https://testofthings.com).
19
+ It was originally created as part of Rauli Kaksonen's doctoral thesis "[Transparent and tool-driven security assessment for sustainable IoT cybersecurity](https://urn.fi/URN:NBN:fi:oulu-202406264941)" under the name of 'tcsfw'.
20
+ The thesis introduces _Tool-driven security assessment_ (TDSA) to enhance the cybersecurity of
21
+ IoT products and systems.
22
+
23
+ ## How Toolsaf and TDSA work?
24
+
25
+ The basic idea of TDSA is to first describe the IoT system or product with
26
+ the security statement, then use tools to verify that the security statement is accurate, and finally use
27
+ the statement for different purposes.
28
+ A security statement provides a machine-readable description of the system's security,
29
+ which allows automated verification.
30
+
31
+ At the moment, security statements are created by Toolsaf using a dedicated Python-based _Domain-Specific Language_ (DSL).
32
+ (JSON-based format is under development).
33
+ Security statement verification is done by running any of the [supported tools](documentation/Tools.md) and saving their output. Toolsaf uses the tools' output to verify that the security statement is accurate.
34
+ Some tools perform their own security checks, which can be incorporated into the security statement verdict.
35
+
36
+ ## Getting Started with Toolsaf
37
+
38
+ The following shows how to start using Toolsaf.
39
+ For this, you need to perform two tasks:
40
+
41
+ 1. Install the Toolsaf Python module.
42
+ 2. Initialize a new or copy an existing security statement.
43
+
44
+ You need a recent [Python](https://www.python.org/) interpreter, such as 3.10 or newer.
45
+ Most Linux distributions should already come with a suitable one, the command
46
+ name might be `python3` to separate from older Python interpreters.
47
+
48
+ Create virtual environment to install Toolsaf and run it. Then install from PyPI with 'pip'.
49
+
50
+ ```shell
51
+ python3 -m venv .venv
52
+ source .venv/bin/activate # Activate the virtual environment
53
+ pip install toolsaf
54
+ ```
55
+ Now that Toolsaf is installed, you have to create a directory for your first security statement.
56
+ The commands below produce the [expected project structure](documentation/CreatingSecurityStatements.md#project-structure) for your statement.
57
+
58
+ ```shell
59
+ mkdir statement-device
60
+ cd statement-device
61
+ mkdir product
62
+ touch product/statement.py # This is the 'security statement'!
63
+ ```
64
+
65
+ Alternatively, you may copy an existing security statement.
66
+ In that case you must also create and activate the virtual environment and install the Toolsaf module.
67
+
68
+ If you want to visualize your security statements you also need to install [Graphviz](https://graphviz.org/download/).
69
+
70
+ In the above, we used _venv_ virtual environments, but as security statements are essentially
71
+ Python-code, any other environment should work fine.
72
+
73
+ ## Working with Security Statements
74
+
75
+ To recap, a security statement is Python DSL code in its own directory, which uses the Toolsaf Python module.
76
+ There are two basic approaches to create a security statement:
77
+
78
+ 1. Write the security statement code first, then run tools and verify it.
79
+ 2. Run tools to collect information first, then write security statement to match the findings.
80
+
81
+ If you’re the device manufacturer you will likely use the former approach, as you (hopefully)
82
+ know your product's attack surface and other properties.
83
+ If you are a security researcher who is reverse-engineering or pen-testing a product,
84
+ you have to use the latter approach.
85
+
86
+ The following documents describe security statements creation and verification:
87
+
88
+ - [Creating Security Statements](documentation/CreatingSecurityStatements.md)
89
+ - [Verifying Security Statements](documentation/VerifyingSecurityStatements.md)
90
+
91
+ ## Command Line Options
92
+
93
+ Toolsaf is a command-line tool, and its command-line options are listed [here](documentation/CommandLineOptions.md).
94
+
95
+ ## Sample Security Statements
96
+ Minimal security statement examples are available in the `samples` directory. They can be executed like this:
97
+ ```shell
98
+ python samples/device-backend/statement.py
99
+ ```
100
+ The command outputs basic information about the security statement.
101
+
102
+ Security statements for **real devices** are available in their own repositories:
103
+ - [Ruuvi Gateway & Tags](https://github.com/testofthings/statement-ruuvi/)
104
+ - [Deltaco Smart Outdoor Plug](https://github.com/testofthings/statement-deltaco-smart-outdoor-plug)
105
+ - [IPC360](https://github.com/testofthings/statement-IPC360)
106
+
107
+ ## Contact
108
+
109
+ Contact Toolsaf team by mail <code>&#116;&#111;&#111;&#108;&#115;&#97;&#102;&#32;&#97;&#116;&#32;&#116;&#101;&#115;&#116;&#111;&#102;&#116;&#104;&#105;&#110;&#103;&#115;&#46;&#99;&#111;&#109;</code>
110
+
111
+ ## License and Contributions
112
+
113
+ The project is published with [MIT license](LICENSE).
114
+
115
+ We are happy to accept contributions to Toolsaf, please see the [instructions](documentation/Contributing.md).
@@ -0,0 +1,89 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ # Project metadata
6
+ [project]
7
+ name = "toolsaf"
8
+ version = "0.2.0"
9
+ requires-python = ">=3.10"
10
+ description = "Tool-Driven Security Assesment Framework"
11
+ readme = "Info.md"
12
+ license = {text = "MIT License"}
13
+ authors = [
14
+ {name = "Rauli Kaksonen", email = "rauli.kaksonen@testofthings.com"}
15
+ ]
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ "Operating System :: OS Independent",
19
+ ]
20
+ dependencies = [
21
+ "packet-framing",
22
+ "censys",
23
+ "aiohttp",
24
+ "aiofiles",
25
+ "sqlalchemy",
26
+ "watchdog",
27
+ "colored",
28
+ "diagrams",
29
+ "shodan"
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ dev = [
34
+ "pytest",
35
+ "pylint",
36
+ "mypy"
37
+ ]
38
+
39
+ [project.urls]
40
+ Repository = "https://github.com/testofthings/toolsaf.git"
41
+ Documentation = "https://github.com/testofthings/toolsaf/tree/main/documentation"
42
+ Issues = "https://github.com/testofthings/toolsaf/issues"
43
+
44
+ # Setuptools config
45
+ [tool.setuptools.packages]
46
+ find = { where = ["."], include = [
47
+ "toolsaf",
48
+ "toolsaf.adapters",
49
+ "toolsaf.common",
50
+ "toolsaf.common.serializer",
51
+ "toolsaf.core",
52
+ "toolsaf.core.serializer"
53
+ ] }
54
+
55
+ [tool.setuptools.package-data]
56
+ "toolsaf.adapters" = ["data/*.json"]
57
+ "toolsaf" = ["diagram_visualizer/*.png"]
58
+
59
+ [tool.setuptools]
60
+ include-package-data = true
61
+
62
+ # Pytest config
63
+ [tool.pytest.ini_options]
64
+ pythonpath = "."
65
+
66
+ # Pylint config
67
+ [tool.pylint.format]
68
+ max-line-length = 120
69
+
70
+ [tool.pylint.messages_control]
71
+ disable = [
72
+ "too-few-public-methods",
73
+ "too-many-arguments",
74
+ "too-many-public-methods",
75
+ "too-many-locals",
76
+ "too-many-instance-attributes",
77
+ "too-many-branches",
78
+ "too-many-statements",
79
+ "too-many-lines",
80
+ "too-many-positional-arguments",
81
+ "too-many-return-statements"
82
+ ]
83
+
84
+ # Mypy config
85
+ [tool.mypy]
86
+ show_error_codes = true
87
+ disable_error_code = ["import-untyped"]
88
+ strict = true
89
+ follow_imports = "silent"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,312 @@
1
+ from typing import Optional
2
+ from ipaddress import IPv4Network
3
+ from toolsaf.common.address import (
4
+ Addresses, DNSName, EndpointAddress, EntityTag, HWAddress, HWAddresses, PseudoAddress,
5
+ IPAddress, IPAddresses, Network, Protocol, AddressSequence, AddressSegment, AnyAddress
6
+ )
7
+ from toolsaf.common.traffic import Protocol
8
+ from toolsaf.main import HTTP, TCP, BLEAdvertisement
9
+ from tests.test_model import Setup
10
+
11
+
12
+ def test_hw_address():
13
+ ad = HWAddress("00:11:22:33:44:55")
14
+ assert f"{ad}" == "00:11:22:33:44:55"
15
+ assert ad.get_parseable_value() == "00:11:22:33:44:55|hw"
16
+ assert ad.is_null() is False
17
+ assert ad.is_global() is False
18
+ assert ad == HWAddress.new("00:11:22:33:44:55")
19
+ assert ad == HWAddress.new("0:11:22:33:44:55")
20
+
21
+ assert HWAddresses.NULL == HWAddress.new("00:00:00:00:00:00")
22
+ assert HWAddresses.NULL.is_null() is True
23
+
24
+
25
+ def test_ip_address():
26
+ ad = IPAddress.new("1.2.3.4")
27
+ assert f"{ad}" == "1.2.3.4"
28
+ assert ad.get_parseable_value() == "1.2.3.4"
29
+ assert ad.is_null() is False
30
+ assert ad.is_global() is True
31
+
32
+ assert IPAddress.new("0.0.0.0") == IPAddresses.NULL
33
+ assert IPAddresses.NULL.is_null() is True
34
+ assert IPAddresses.NULL.is_global() is False
35
+
36
+ assert IPAddress.new("192.168.1.1").is_global() is False
37
+
38
+
39
+ def test_dns_name():
40
+ ad = DNSName("www.example.com")
41
+ assert f"{ad}" == "www.example.com"
42
+ assert ad.get_parseable_value() == "www.example.com|name"
43
+ assert ad.is_null() is False
44
+ assert ad.is_global() is True
45
+ assert ad == DNSName("www.example.com")
46
+ assert ad != DNSName("www.example.org")
47
+
48
+
49
+ def test_endpoint_address():
50
+ ad = EndpointAddress.ip("1.2.3.4", Protocol.UDP, 1234)
51
+ assert f"{ad}" == "1.2.3.4/udp:1234"
52
+ assert ad.get_parseable_value() == "1.2.3.4/udp:1234"
53
+ assert ad.get_host() == IPAddress.new("1.2.3.4")
54
+ assert ad.protocol == Protocol.UDP
55
+ assert ad.port == 1234
56
+
57
+ ad = EndpointAddress.hw("0:1:2:3:4:5", Protocol.UDP, 1234)
58
+ assert f"{ad}" == "00:01:02:03:04:05/udp:1234"
59
+ assert ad.get_parseable_value() == "00:01:02:03:04:05|hw/udp:1234"
60
+
61
+
62
+ def test_parse_address():
63
+ a = Addresses.parse_address("1.2.3.4")
64
+ assert isinstance(a, IPAddress)
65
+ assert f"{a}" == "1.2.3.4"
66
+
67
+ a = Addresses.parse_address("www.example.com|name")
68
+ assert isinstance(a, DNSName)
69
+ assert f"{a}" == "www.example.com"
70
+
71
+ a = Addresses.parse_address("1:2:3:4:5:6|hw")
72
+ assert isinstance(a, HWAddress)
73
+ assert f"{a}" == "01:02:03:04:05:06"
74
+
75
+
76
+ def test_parse_endpoint_address():
77
+ a = Addresses.parse_endpoint("1.2.3.4/udp:1234")
78
+ assert isinstance(a, EndpointAddress)
79
+ assert f"{a}" == "1.2.3.4/udp:1234"
80
+ assert a.get_host() == IPAddress.new("1.2.3.4")
81
+ assert a.protocol == Protocol.UDP
82
+ assert a.port == 1234
83
+
84
+ a = Addresses.parse_endpoint("1:2:3:4:5:6|hw/HTTP")
85
+ assert f"{a}" == "01:02:03:04:05:06/http"
86
+ assert a.get_host() == HWAddress.new("01:02:03:04:05:06")
87
+ assert a.protocol == Protocol.HTTP
88
+ assert a.port == -1
89
+
90
+ ad = EndpointAddress(EntityTag.new("xxx"), Protocol.ETHERNET, 35000)
91
+ assert f"{ad}" == "xxx/eth:35000"
92
+ ad2 = Addresses.parse_endpoint(ad.get_parseable_value())
93
+ assert ad == ad2
94
+
95
+
96
+ def test_hw_address_generation():
97
+ ip = IPAddress.new("192.168.0.2")
98
+ hw = HWAddress.from_ip(ip)
99
+ assert hw == HWAddress('40:00:c0:a8:00:02')
100
+
101
+
102
+ def test_ip_network_matching():
103
+ nw = Network("net", ip_network=IPv4Network("22.33.0.0/16"))
104
+ assert not nw.is_local(IPAddress.new("22.2.3.4"))
105
+ assert nw.is_local(IPAddress.new("22.33.3.4"))
106
+ assert nw.is_local(IPAddress.new("22.33.33.4"))
107
+
108
+ nw = Network("net", ip_network=IPv4Network("0.0.0.0/0"))
109
+ assert nw.is_local(IPAddress.new("22.33.3.4"))
110
+ assert nw.is_local(IPAddress.new("22.2.3.4"))
111
+ assert nw.is_local(IPAddress.new("22.33.3.4"))
112
+ assert nw.is_local(IPAddress.new("22.33.33.4"))
113
+
114
+
115
+ def _segment(address: AnyAddress, segment_type: Optional[str]=None) -> AddressSegment:
116
+ return AddressSegment(address, segment_type)
117
+
118
+
119
+ def test_address_sequence():
120
+ addr1 = EntityTag.new("Test1")
121
+ addr2 = EndpointAddress(EntityTag("Test2"), Protocol.TCP, 80)
122
+ seq = AddressSequence.new(addr1, addr2)
123
+ assert seq.segments == (_segment(addr1), _segment(addr2))
124
+
125
+
126
+ def test_address_sequence_get_parseable_value():
127
+ addr1 = _segment(EntityTag.new("Test1"))
128
+ addr2 = _segment(EndpointAddress(EntityTag("Test2"), Protocol.TCP, 80))
129
+ addr3 = _segment(EntityTag("Test_SW"), segment_type="software")
130
+
131
+ assert AddressSequence([addr1]).get_parseable_value() == "Test1"
132
+ assert AddressSequence([addr2]).get_parseable_value() == "Test2/tcp:80"
133
+ assert AddressSequence([addr1, addr2, addr3]).get_parseable_value() == "Test1&Test2/tcp:80&software=Test_SW"
134
+
135
+ addr2.segment_type = "source"
136
+ addr4 = _segment(EndpointAddress(EntityTag("Test4"), Protocol.UDP, 123), segment_type="target")
137
+ assert AddressSequence([addr2, addr4]).get_parseable_value() == \
138
+ "source=Test2/tcp:80&target=Test4/udp:123"
139
+
140
+
141
+ def test_get_system_address():
142
+ system = Setup().system
143
+ system.system.name = "Test System"
144
+ device = system.device("D")
145
+
146
+ # IoTSystem - empty sequence
147
+ assert system.system.get_system_address() == AddressSequence((),)
148
+
149
+ # Host
150
+ assert device.entity.get_system_address() == AddressSequence.new(
151
+ EntityTag.new("D")
152
+ )
153
+
154
+ # Service
155
+ service = (device / HTTP).entity
156
+ assert service.get_system_address() == AddressSequence.new(
157
+ EntityTag("D"), EndpointAddress(Addresses.ANY, Protocol.TCP, 80)
158
+ )
159
+
160
+ # NodeComponent
161
+ software = device.software("SW").get_software()
162
+ assert software.get_system_address() == AddressSequence((
163
+ _segment(EntityTag("D")), _segment(EntityTag("SW"), segment_type="software")
164
+ ))
165
+
166
+ # Connection
167
+ backend = system.backend("B")
168
+ connection = (device >> backend / HTTP).connection
169
+ assert connection.get_system_address() == AddressSequence((
170
+ _segment(EntityTag("D"), "source"),
171
+ _segment(EntityTag("B"), "target"), _segment(EndpointAddress(Addresses.ANY, Protocol.TCP, 80)),
172
+ ))
173
+
174
+ connection = ((device / HTTP) >> backend / TCP(port=111)).connection
175
+ assert connection.get_system_address() == AddressSequence((
176
+ _segment(EntityTag("D"), "source"), _segment(EndpointAddress(Addresses.ANY, Protocol.TCP, 80)),
177
+ _segment(EntityTag("B"), "target"), _segment(EndpointAddress(Addresses.ANY, Protocol.TCP, 111)),
178
+ ))
179
+
180
+ ble_ad = device.broadcast(BLEAdvertisement(event_type=0x03))
181
+ assert ble_ad.entity.get_system_address() == AddressSequence.new(
182
+ EntityTag("D"), EndpointAddress(Addresses.ANY, Protocol.BLE, 3)
183
+ )
184
+
185
+ connection = (backend << ble_ad).connection
186
+ assert connection.get_system_address() == AddressSequence((
187
+ _segment(EntityTag("D"), "source"),
188
+ _segment(EntityTag("B"), "target"), _segment(EndpointAddress(PseudoAddress("BLE_Ad"), Protocol.BLE, 3)),
189
+ ))
190
+
191
+
192
+ def test_parse_system_address():
193
+ assert Addresses.parse_system_address(
194
+ "1.2.3.4"
195
+ ) == AddressSequence.new(IPAddress.new("1.2.3.4"))
196
+
197
+ assert Addresses.parse_system_address(
198
+ "1:2:3:4:5:6|hw"
199
+ ) == AddressSequence.new(HWAddress.new("1:2:3:4:5:6"))
200
+
201
+ assert Addresses.parse_system_address(
202
+ "Test_Device"
203
+ ) == AddressSequence.new(EntityTag("Test_Device"))
204
+
205
+ assert Addresses.parse_system_address(
206
+ "1.2.3.4/udp:1234"
207
+ ) == AddressSequence.new(EndpointAddress(IPAddress.new("1.2.3.4"), Protocol.UDP, 1234))
208
+
209
+ assert Addresses.parse_system_address(
210
+ "Test_Device/tcp:80"
211
+ ) == AddressSequence.new(EndpointAddress(EntityTag("Test_Device"), Protocol.TCP, 80))
212
+
213
+ assert Addresses.parse_system_address(
214
+ "ff_ff_ff_ff_ff_ff/arp"
215
+ ) == AddressSequence.new(EndpointAddress(EntityTag("ff_ff_ff_ff_ff_ff"), Protocol.ARP))
216
+
217
+ assert Addresses.parse_system_address(
218
+ "Test_Device&software=Test_SW"
219
+ ) == AddressSequence((
220
+ _segment(EntityTag("Test_Device")), _segment(EntityTag("Test_SW"), "software")
221
+ ))
222
+
223
+ assert Addresses.parse_system_address(
224
+ "source=Test_Device&target=Test_Device/tcp:80"
225
+ ) == AddressSequence((
226
+ _segment(EntityTag("Test_Device"), "source"), _segment(EndpointAddress(EntityTag("Test_Device"), Protocol.TCP, 80), "target")
227
+ ))
228
+
229
+ assert Addresses.parse_system_address(
230
+ "source=Test_Device/udp:123&target=Test_Device"
231
+ ) == AddressSequence((
232
+ _segment(EndpointAddress(EntityTag("Test_Device"), Protocol.UDP, 123), "source"), _segment(EntityTag("Test_Device"), "target")
233
+ ))
234
+
235
+ assert Addresses.parse_system_address(
236
+ "source=Test_Device/tcp:80&target=Test_Device/udp:123"
237
+ ) == AddressSequence((
238
+ _segment(EndpointAddress(EntityTag("Test_Device"), Protocol.TCP, 80), "source"),
239
+ _segment(EndpointAddress(EntityTag("Test_Device"), Protocol.UDP, 123), "target")
240
+ ))
241
+
242
+ assert Addresses.parse_system_address(
243
+ "source=1.2.3.4&target=Test_Device/tcp:80"
244
+ ) == AddressSequence((
245
+ _segment(IPAddress.new("1.2.3.4"), "source"), _segment(EndpointAddress(EntityTag("Test_Device"), Protocol.TCP, 80), "target")
246
+ ))
247
+
248
+ assert Addresses.parse_system_address(
249
+ "source=1.2.3.4/tcp:80&target=01:01:01:01:01:01|hw/tcp:80"
250
+ ) == AddressSequence((
251
+ _segment(EndpointAddress(IPAddress.new("1.2.3.4"), Protocol.TCP, 80), "source"),
252
+ _segment(EndpointAddress(HWAddress.new("01:01:01:01:01:01"), Protocol.TCP, 80), "target")
253
+ ))
254
+
255
+ assert Addresses.parse_system_address(
256
+ "Test_Device/tcp:80&software=Test_SW"
257
+ ) == AddressSequence((
258
+ _segment(EndpointAddress(EntityTag("Test_Device"), Protocol.TCP, 80)), _segment(EntityTag("Test_SW"), "software")
259
+ ))
260
+
261
+ assert Addresses.parse_system_address(
262
+ "Test/tcp:80&software=VM&VirtualEnv/udp:123"
263
+ ) == AddressSequence((
264
+ _segment(EndpointAddress(EntityTag("Test"), Protocol.TCP, 80)),
265
+ _segment(EntityTag("VM"), "software"),
266
+ _segment(EndpointAddress(EntityTag("VirtualEnv"), Protocol.UDP, 123))
267
+ ))
268
+
269
+ assert Addresses.parse_system_address(
270
+ "Test/ble:3"
271
+ ) == AddressSequence((
272
+ _segment(EndpointAddress(EntityTag("Test"), Protocol.BLE, 3)),
273
+ ))
274
+
275
+ assert Addresses.parse_system_address(
276
+ 'source=Test1&target=Test2/ble:3'
277
+ ) == AddressSequence((
278
+ _segment(EntityTag("Test1"), "source"),
279
+ _segment(EndpointAddress(EntityTag("Test2"), Protocol.BLE, 3), "target")
280
+ ))
281
+
282
+
283
+ def test_system_endpoint_address():
284
+ # Endpoint address with host
285
+ ep = AddressSequence.new(EndpointAddress(EntityTag("Device"), Protocol.TCP, 1234))
286
+ # Endpoint address ANY host, as it is used in services
287
+ ep_any = AddressSequence.new(EntityTag("Device"), EndpointAddress(Addresses.ANY, Protocol.TCP, 1234))
288
+ # Explicit endpoint address for a service
289
+ ep_exp = AddressSequence.new(EntityTag("Device"), EndpointAddress(IPAddresses.BROADCAST, Protocol.TCP, 1234))
290
+
291
+ ep_s = ep.get_parseable_value()
292
+ ep_any_s = ep_any.get_parseable_value()
293
+ ep_exp_s = ep_exp.get_parseable_value()
294
+ assert ep_s == "Device/tcp:1234"
295
+ assert ep_any_s == "Device/tcp:1234"
296
+ assert ep_exp_s == "Device&255.255.255.255/tcp:1234"
297
+
298
+ ep2 = Addresses.parse_system_address(ep_s)
299
+ ep_any2 = Addresses.parse_system_address(ep_any_s)
300
+ ep_exp2 = Addresses.parse_system_address(ep_exp_s)
301
+ assert ep2 == ep
302
+ assert ep_any2 == ep # Parses into same as ep!
303
+ assert ep_exp2 == ep_exp2
304
+
305
+
306
+ def test_system_endpoint_address_no_protocol():
307
+ ep = AddressSequence.new(EntityTag("Device"), EndpointAddress(Addresses.ANY, Protocol.ETHERNET))
308
+ ep_str = ep.get_parseable_value()
309
+ assert ep_str == "Device/eth"
310
+ # parsed into one entity tag
311
+ ep2 = Addresses.parse_system_address(ep_str)
312
+ assert [s.address for s in ep2.segments] == [EndpointAddress(EntityTag("Device"), Protocol.ETHERNET)]
@@ -0,0 +1,40 @@
1
+ from toolsaf.common.address import Protocol
2
+ from toolsaf.builder_backend import SystemBackend
3
+ from toolsaf.core.inspector import Inspector
4
+ from toolsaf.main import ARP
5
+ from toolsaf.common.basics import ExternalActivity
6
+ from toolsaf.common.traffic import EthernetFlow
7
+ from toolsaf.common.basics import Status
8
+
9
+
10
+ def test_serve_arp():
11
+ sb = SystemBackend()
12
+ dev1 = sb.device().hw("1:0:0:0:0:1")
13
+ dev1.serve(ARP)
14
+ dev2 = sb.device().hw("1:0:0:0:0:2")
15
+ dev3 = sb.device().hw("1:0:0:0:0:3")
16
+ dev3.entity.set_external_activity(ExternalActivity.UNLIMITED)
17
+ dev4 = sb.device().hw("1:0:0:0:0:4")
18
+ dev4 >> dev1 / ARP
19
+
20
+ m = Inspector(sb.system)
21
+
22
+ # dev3 can make external calls
23
+ f1 = m.connection(EthernetFlow.new(Protocol.ARP, "1:0:0:0:0:3") >> "ff:ff:ff:ff:ff:ff")
24
+ assert f1.status == Status.EXTERNAL
25
+
26
+ # dev2 not defined to make ARP calls
27
+ f1 = m.connection(EthernetFlow.new(Protocol.ARP, "1:0:0:0:0:2") >> "ff:ff:ff:ff:ff:ff")
28
+ assert f1.status == Status.UNEXPECTED
29
+
30
+ # unknown device can make ARP calls
31
+ f1 = m.connection(EthernetFlow.new(Protocol.ARP, "1:0:0:0:1:1") >> "ff:ff:ff:ff:ff:ff")
32
+ assert f1.status == Status.EXTERNAL
33
+
34
+ # dev4 can make ARP calls
35
+ f1 = m.connection(EthernetFlow.new(Protocol.ARP, "1:0:0:0:0:4") >> "ff:ff:ff:ff:ff:ff")
36
+ assert f1.status == Status.EXPECTED
37
+ # FIXME: The remaining does not work
38
+ f1 = m.connection(EthernetFlow.new(Protocol.ARP, "1:0:0:0:0:4") << "1:0:0:0:0:1")
39
+ # assert f1.status == Status.EXPECTED
40
+