regscale-cli 6.23.0.1__py3-none-any.whl → 6.24.0.0__py3-none-any.whl
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.
Potentially problematic release.
This version of regscale-cli might be problematic. Click here for more details.
- regscale/_version.py +1 -1
- regscale/core/app/application.py +2 -0
- regscale/integrations/commercial/__init__.py +1 -0
- regscale/integrations/commercial/sarif/sarif_converter.py +1 -1
- regscale/integrations/commercial/wizv2/click.py +109 -2
- regscale/integrations/commercial/wizv2/compliance_report.py +1485 -0
- regscale/integrations/commercial/wizv2/constants.py +72 -2
- regscale/integrations/commercial/wizv2/data_fetcher.py +61 -0
- regscale/integrations/commercial/wizv2/file_cleanup.py +104 -0
- regscale/integrations/commercial/wizv2/issue.py +775 -27
- regscale/integrations/commercial/wizv2/policy_compliance.py +599 -181
- regscale/integrations/commercial/wizv2/reports.py +243 -0
- regscale/integrations/commercial/wizv2/scanner.py +668 -245
- regscale/integrations/compliance_integration.py +304 -51
- regscale/integrations/due_date_handler.py +210 -0
- regscale/integrations/public/cci_importer.py +444 -0
- regscale/integrations/scanner_integration.py +718 -153
- regscale/models/integration_models/CCI_List.xml +1 -0
- regscale/models/integration_models/cisa_kev_data.json +18 -3
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/regscale_models/form_field_value.py +1 -1
- regscale/models/regscale_models/milestone.py +1 -0
- regscale/models/regscale_models/regscale_model.py +225 -60
- regscale/models/regscale_models/security_plan.py +3 -2
- regscale/regscale.py +7 -0
- {regscale_cli-6.23.0.1.dist-info → regscale_cli-6.24.0.0.dist-info}/METADATA +9 -9
- {regscale_cli-6.23.0.1.dist-info → regscale_cli-6.24.0.0.dist-info}/RECORD +43 -26
- tests/fixtures/test_fixture.py +13 -8
- tests/regscale/integrations/public/__init__.py +0 -0
- tests/regscale/integrations/public/test_alienvault.py +220 -0
- tests/regscale/integrations/public/test_cci.py +458 -0
- tests/regscale/integrations/public/test_cisa.py +1021 -0
- tests/regscale/integrations/public/test_emass.py +518 -0
- tests/regscale/integrations/public/test_fedramp.py +851 -0
- tests/regscale/integrations/public/test_fedramp_cis_crm.py +3661 -0
- tests/regscale/integrations/public/test_file_uploads.py +506 -0
- tests/regscale/integrations/public/test_oscal.py +453 -0
- tests/regscale/models/test_form_field_value_integration.py +304 -0
- tests/regscale/models/test_module_integration.py +582 -0
- {regscale_cli-6.23.0.1.dist-info → regscale_cli-6.24.0.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.23.0.1.dist-info → regscale_cli-6.24.0.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.23.0.1.dist-info → regscale_cli-6.24.0.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.23.0.1.dist-info → regscale_cli-6.24.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,851 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from unittest.mock import MagicMock, mock_open, patch
|
|
6
|
+
from xml.etree.ElementTree import Element
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
from docx import Document
|
|
10
|
+
from lxml import etree
|
|
11
|
+
|
|
12
|
+
from regscale.integrations.public.fedramp.fedramp_cis_crm import gen_key
|
|
13
|
+
from regscale.integrations.public.fedramp.fedramp_common import (
|
|
14
|
+
check_profile,
|
|
15
|
+
create_component_mapping,
|
|
16
|
+
create_port,
|
|
17
|
+
create_ssp_components,
|
|
18
|
+
get_base_contact,
|
|
19
|
+
get_profile,
|
|
20
|
+
get_tables,
|
|
21
|
+
get_text_between_headers,
|
|
22
|
+
get_xpath_data_detailed,
|
|
23
|
+
post_links,
|
|
24
|
+
post_regscale_object,
|
|
25
|
+
validate_date_str,
|
|
26
|
+
)
|
|
27
|
+
from regscale.integrations.public.fedramp.fedramp_five import extract_parts
|
|
28
|
+
from regscale.integrations.public.fedramp.import_workbook import _update_ip_addresses, determine_ip_address_version
|
|
29
|
+
from regscale.models.regscale_models.component import Component
|
|
30
|
+
from regscale.models.regscale_models.control_implementation import (
|
|
31
|
+
ControlImplementation,
|
|
32
|
+
ControlImplementationOrigin,
|
|
33
|
+
ControlImplementationStatus,
|
|
34
|
+
)
|
|
35
|
+
from regscale.models.regscale_models.ports_protocol import PortsProtocol
|
|
36
|
+
from tests import CLITestFixture
|
|
37
|
+
from tests.mocks.response import create_mock_response
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class TestFedRamp(CLITestFixture):
|
|
41
|
+
mock_ssp_id = 123
|
|
42
|
+
existing_links = []
|
|
43
|
+
content_type = "application/json"
|
|
44
|
+
accept = content_type
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
@pytest.fixture
|
|
48
|
+
def element():
|
|
49
|
+
# This creates a new Element object for each test
|
|
50
|
+
return Element("Control")
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
@pytest.fixture
|
|
54
|
+
def implementation():
|
|
55
|
+
# This provides a fresh instance of ControlImplementation for each test
|
|
56
|
+
return ControlImplementation(
|
|
57
|
+
**{
|
|
58
|
+
"id": 3367,
|
|
59
|
+
"uuid": "28485bbd-ea6a-400f-9211-8be5e792d05d",
|
|
60
|
+
"isPublic": True,
|
|
61
|
+
"inheritable": True,
|
|
62
|
+
"controlOwner": "Sumka, James",
|
|
63
|
+
"controlOwnerId": "758e40b9-8f47-498f-9581-bb1f2f63c52f",
|
|
64
|
+
"systemRole": "Service Provider Management",
|
|
65
|
+
"policy": "",
|
|
66
|
+
"implementation": "My implemen",
|
|
67
|
+
"cloudImplementation": "",
|
|
68
|
+
"status": "Fully Implemented",
|
|
69
|
+
"stepsToImplement": "",
|
|
70
|
+
"lastAssessmentResult": "",
|
|
71
|
+
"controlName": "15.3",
|
|
72
|
+
"controlTitle": "Classify Service Providers",
|
|
73
|
+
"controlID": 10797,
|
|
74
|
+
"controlSource": "Baseline",
|
|
75
|
+
"exclusionJustification": "",
|
|
76
|
+
"practiceLevel": "",
|
|
77
|
+
"processLevel": "",
|
|
78
|
+
"cyberFunction": "",
|
|
79
|
+
"implementationType": "",
|
|
80
|
+
"implementationMethod": "",
|
|
81
|
+
"qdWellDesigned": "",
|
|
82
|
+
"qdProcedures": "",
|
|
83
|
+
"qdSegregation": "",
|
|
84
|
+
"qdFlowdown": "",
|
|
85
|
+
"qdAutomated": "",
|
|
86
|
+
"qdOverall": "",
|
|
87
|
+
"qiResources": "",
|
|
88
|
+
"qiMaturity": "",
|
|
89
|
+
"qiReporting": "",
|
|
90
|
+
"qiVendorCompliance": "",
|
|
91
|
+
"qiIssues": "",
|
|
92
|
+
"qiOverall": "",
|
|
93
|
+
"bBaseline": False,
|
|
94
|
+
"bInherited": False,
|
|
95
|
+
"bOverlay": False,
|
|
96
|
+
"bTailored": False,
|
|
97
|
+
"bStatusImplemented": False,
|
|
98
|
+
"bStatusPartiallyImplemented": False,
|
|
99
|
+
"bStatusPlanned": False,
|
|
100
|
+
"bStatusAlternative": False,
|
|
101
|
+
"bStatusNotApplicable": False,
|
|
102
|
+
"bServiceProviderCorporate": False,
|
|
103
|
+
"bServiceProviderSystemSpecific": False,
|
|
104
|
+
"bServiceProviderHybrid": False,
|
|
105
|
+
"bConfiguredByCustomer": False,
|
|
106
|
+
"bProvidedByCustomer": False,
|
|
107
|
+
"bShared": False,
|
|
108
|
+
"bInheritedFedrampAuthorization": False,
|
|
109
|
+
"responsibility": "Customer",
|
|
110
|
+
"assessmentFrequency": 365,
|
|
111
|
+
"parentId": 22,
|
|
112
|
+
"parentModule": "securityplans",
|
|
113
|
+
"createdById": "758e40b9-8f47-498f-9581-bb1f2f63c52f",
|
|
114
|
+
"dateCreated": "2024-01-08T17:50:07.9305152",
|
|
115
|
+
"lastUpdatedById": "758e40b9-8f47-498f-9581-bb1f2f63c52f",
|
|
116
|
+
"dateLastUpdated": "2024-02-07T13:54:45.5246858",
|
|
117
|
+
"weight": 0,
|
|
118
|
+
"parameters": [],
|
|
119
|
+
"tests": [],
|
|
120
|
+
"objectives": [],
|
|
121
|
+
"family": "Service Provider Management",
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
@pytest.fixture
|
|
127
|
+
def example_table():
|
|
128
|
+
"""Create an example XML table"""
|
|
129
|
+
xml_string = """
|
|
130
|
+
<w:tbl xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex" xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex" xmlns:cx2="http://schemas.microsoft.com/office/drawing/2015/10/21/chartex" xmlns:cx3="http://schemas.microsoft.com/office/drawing/2016/5/9/chartex" xmlns:cx4="http://schemas.microsoft.com/office/drawing/2016/5/10/chartex" xmlns:cx5="http://schemas.microsoft.com/office/drawing/2016/5/11/chartex" xmlns:cx6="http://schemas.microsoft.com/office/drawing/2016/5/12/chartex" xmlns:cx7="http://schemas.microsoft.com/office/drawing/2016/5/13/chartex" xmlns:cx8="http://schemas.microsoft.com/office/drawing/2016/5/14/chartex" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:aink="http://schemas.microsoft.com/office/drawing/2016/ink" xmlns:am3d="http://schemas.microsoft.com/office/drawing/2017/model3d" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:oel="http://schemas.microsoft.com/office/2019/extlst" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml" xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape"><w:tblPr><w:tblW w:w="5000" w:type="pct"/><w:jc w:val="center"/><w:tblBorders><w:top w:val="single" w:sz="4" w:space="0" w:color="969996" w:themeColor="text1" w:themeTint="80"/><w:left w:val="single" w:sz="4" w:space="0" w:color="969996" w:themeColor="text1" w:themeTint="80"/><w:bottom w:val="single" w:sz="4" w:space="0" w:color="969996" w:themeColor="text1" w:themeTint="80"/><w:right w:val="single" w:sz="4" w:space="0" w:color="969996" w:themeColor="text1" w:themeTint="80"/><w:insideH w:val="single" w:sz="4" w:space="0" w:color="969996" w:themeColor="text1" w:themeTint="80"/><w:insideV w:val="single" w:sz="4" w:space="0" w:color="969996" w:themeColor="text1" w:themeTint="80"/></w:tblBorders><w:tblCellMar><w:top w:w="72" w:type="dxa"/><w:left w:w="72" w:type="dxa"/><w:bottom w:w="72" w:type="dxa"/><w:right w:w="72" w:type="dxa"/></w:tblCellMar><w:tblLook w:val="0000" w:firstRow="0" w:lastRow="0" w:firstColumn="0" w:lastColumn="0" w:noHBand="0" w:noVBand="0"/></w:tblPr><w:tblGrid><w:gridCol w:w="3541"/><w:gridCol w:w="9409"/></w:tblGrid><w:tr w:rsidR="00CC17A4" w:rsidRPr="00551FB0" w14:paraId="25F8059F" w14:textId="77777777" w:rsidTr="00551FB0"><w:trPr><w:cantSplit/><w:trHeight w:val="20"/><w:tblHeader/><w:jc w:val="center"/></w:trPr><w:tc><w:tcPr><w:tcW w:w="1367" w:type="pct"/><w:shd w:val="clear" w:color="auto" w:fill="1D396B" w:themeFill="accent5"/><w:tcMar><w:top w:w="0" w:type="dxa"/><w:left w:w="101" w:type="dxa"/><w:bottom w:w="115" w:type="dxa"/><w:right w:w="101" w:type="dxa"/></w:tcMar><w:vAlign w:val="center"/></w:tcPr><w:p w14:paraId="37EBFE1E" w14:textId="77777777" w:rsidR="00CC17A4" w:rsidRPr="00551FB0" w:rsidRDefault="00CC17A4" w:rsidP="0094101B"><w:pPr><w:pStyle w:val="GSATableHeading"/><w:rPr><w:rFonts w:asciiTheme="majorHAnsi" w:hAnsiTheme="majorHAnsi"/><w:szCs w:val="20"/></w:rPr></w:pPr><w:r w:rsidRPr="00551FB0"><w:rPr><w:rFonts w:asciiTheme="majorHAnsi" w:hAnsiTheme="majorHAnsi"/><w:szCs w:val="20"/></w:rPr><w:t>Security Objective</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:w="3633" w:type="pct"/><w:shd w:val="clear" w:color="auto" w:fill="1D396B" w:themeFill="accent5"/><w:tcMar><w:top w:w="0" w:type="dxa"/><w:left w:w="101" w:type="dxa"/><w:bottom w:w="115" w:type="dxa"/><w:right w:w="101" w:type="dxa"/></w:tcMar><w:vAlign w:val="center"/></w:tcPr><w:p w14:paraId="73808EED" w14:textId="77777777" w:rsidR="00CC17A4" w:rsidRPr="00551FB0" w:rsidRDefault="00CC17A4" w:rsidP="0094101B"><w:pPr><w:pStyle w:val="GSATableHeading"/><w:rPr><w:rFonts w:asciiTheme="majorHAnsi" w:hAnsiTheme="majorHAnsi"/><w:szCs w:val="20"/></w:rPr></w:pPr><w:r w:rsidRPr="00551FB0"><w:rPr><w:rFonts w:asciiTheme="majorHAnsi" w:hAnsiTheme="majorHAnsi"/><w:szCs w:val="20"/></w:rPr><w:t>Low, Moderate or High</w:t></w:r></w:p></w:tc></w:tr><w:tr w:rsidR="00CC17A4" w:rsidRPr="00551FB0" w14:paraId="1208D909" w14:textId="77777777" w:rsidTr="00551FB0"><w:trPr><w:cantSplit/><w:trHeight w:val="20"/><w:tblHeader/><w:jc w:val="center"/></w:trPr><w:tc><w:tcPr><w:tcW w:w="1367" w:type="pct"/><w:shd w:val="clear" w:color="auto" w:fill="auto"/><w:tcMar><w:top w:w="0" w:type="dxa"/><w:left w:w="101" w:type="dxa"/><w:bottom w:w="115" w:type="dxa"/><w:right w:w="101" w:type="dxa"/></w:tcMar></w:tcPr><w:p w14:paraId="36FC716A" w14:textId="77777777" w:rsidR="00CC17A4" w:rsidRPr="00551FB0" w:rsidRDefault="00CC17A4" w:rsidP="00551FB0"><w:pPr><w:pStyle w:val="GSATableHeading"/><w:keepNext w:val="0"/><w:keepLines w:val="0"/><w:rPr><w:szCs w:val="20"/></w:rPr></w:pPr><w:r w:rsidRPr="00551FB0"><w:rPr><w:szCs w:val="20"/></w:rPr><w:t>Confidentiality</w:t></w:r></w:p></w:tc><w:sdt><w:sdtPr><w:rPr><w:sz w:val="20"/><w:szCs w:val="20"/></w:rPr><w:alias w:val="Low Moderate High"/><w:tag w:val="lowmoderatehigh"/><w:id w:val="-1240482717"/><w:placeholder><w:docPart w:val="BBFD3EFCFAB74D189DFADB8014C4B0A2"/></w:placeholder><w:dropDownList><w:listItem w:value="Choose an item."/><w:listItem w:displayText="Low (L)" w:value="Low (L)"/><w:listItem w:displayText="Moderate (M)" w:value="Moderate (M)"/><w:listItem w:displayText="High (H)" w:value="High (H)"/></w:dropDownList></w:sdtPr><w:sdtEndPr/><w:sdtContent><w:tc><w:tcPr><w:tcW w:w="3633" w:type="pct"/><w:shd w:val="clear" w:color="auto" w:fill="auto"/><w:tcMar><w:top w:w="0" w:type="dxa"/><w:left w:w="101" w:type="dxa"/><w:bottom w:w="115" w:type="dxa"/><w:right w:w="101" w:type="dxa"/></w:tcMar></w:tcPr><w:p w14:paraId="0D97C262" w14:textId="43014E66" w:rsidR="00CC17A4" w:rsidRPr="00551FB0" w:rsidRDefault="00D87316" w:rsidP="00551FB0"><w:pPr><w:pStyle w:val="GSATableText"/><w:rPr><w:sz w:val="20"/><w:szCs w:val="20"/></w:rPr></w:pPr><w:r><w:rPr><w:sz w:val="20"/><w:szCs w:val="20"/></w:rPr><w:t>Moderate (M)</w:t></w:r></w:p></w:tc></w:sdtContent></w:sdt></w:tr><w:tr w:rsidR="00CC17A4" w:rsidRPr="00551FB0" w14:paraId="1274421F" w14:textId="77777777" w:rsidTr="00551FB0"><w:trPr><w:cantSplit/><w:trHeight w:val="20"/><w:tblHeader/><w:jc w:val="center"/></w:trPr><w:tc><w:tcPr><w:tcW w:w="1367" w:type="pct"/><w:shd w:val="clear" w:color="auto" w:fill="auto"/><w:tcMar><w:top w:w="0" w:type="dxa"/><w:left w:w="101" w:type="dxa"/><w:bottom w:w="115" w:type="dxa"/><w:right w:w="101" w:type="dxa"/></w:tcMar></w:tcPr><w:p w14:paraId="787AE0EB" w14:textId="77777777" w:rsidR="00CC17A4" w:rsidRPr="00551FB0" w:rsidRDefault="00CC17A4" w:rsidP="00551FB0"><w:pPr><w:pStyle w:val="GSATableHeading"/><w:keepNext w:val="0"/><w:keepLines w:val="0"/><w:rPr><w:szCs w:val="20"/></w:rPr></w:pPr><w:r w:rsidRPr="00551FB0"><w:rPr><w:szCs w:val="20"/></w:rPr><w:t>Integrity</w:t></w:r></w:p></w:tc><w:sdt><w:sdtPr><w:rPr><w:sz w:val="20"/><w:szCs w:val="20"/></w:rPr><w:alias w:val="Low Moderate High"/><w:tag w:val="lowmoderatehigh"/><w:id w:val="-577524295"/><w:placeholder><w:docPart w:val="0BF26A562B574649976781A6EEC52519"/></w:placeholder><w:dropDownList><w:listItem w:value="Choose an item."/><w:listItem w:displayText="Low (L)" w:value="Low (L)"/><w:listItem w:displayText="Moderate (M)" w:value="Moderate (M)"/><w:listItem w:displayText="High (H)" w:value="High (H)"/></w:dropDownList></w:sdtPr><w:sdtEndPr/><w:sdtContent><w:tc><w:tcPr><w:tcW w:w="3633" w:type="pct"/><w:shd w:val="clear" w:color="auto" w:fill="auto"/><w:tcMar><w:top w:w="0" w:type="dxa"/><w:left w:w="101" w:type="dxa"/><w:bottom w:w="115" w:type="dxa"/><w:right w:w="101" w:type="dxa"/></w:tcMar></w:tcPr><w:p w14:paraId="1D138F7C" w14:textId="6E8E85A3" w:rsidR="00CC17A4" w:rsidRPr="00551FB0" w:rsidRDefault="00D87316" w:rsidP="00551FB0"><w:pPr><w:pStyle w:val="GSATableText"/><w:rPr><w:sz w:val="20"/><w:szCs w:val="20"/></w:rPr></w:pPr><w:r><w:rPr><w:sz w:val="20"/><w:szCs w:val="20"/></w:rPr><w:t>Moderate (M)</w:t></w:r></w:p></w:tc></w:sdtContent></w:sdt></w:tr><w:tr w:rsidR="00CC17A4" w:rsidRPr="00551FB0" w14:paraId="7AA0A70F" w14:textId="77777777" w:rsidTr="00551FB0"><w:trPr><w:cantSplit/><w:trHeight w:val="20"/><w:tblHeader/><w:jc w:val="center"/></w:trPr><w:tc><w:tcPr><w:tcW w:w="1367" w:type="pct"/><w:shd w:val="clear" w:color="auto" w:fill="auto"/><w:tcMar><w:top w:w="0" w:type="dxa"/><w:left w:w="101" w:type="dxa"/><w:bottom w:w="115" w:type="dxa"/><w:right w:w="101" w:type="dxa"/></w:tcMar></w:tcPr><w:p w14:paraId="111949F9" w14:textId="77777777" w:rsidR="00CC17A4" w:rsidRPr="00551FB0" w:rsidRDefault="00CC17A4" w:rsidP="00551FB0"><w:pPr><w:pStyle w:val="GSATableHeading"/><w:keepNext w:val="0"/><w:keepLines w:val="0"/><w:rPr><w:szCs w:val="20"/></w:rPr></w:pPr><w:r w:rsidRPr="00551FB0"><w:rPr><w:szCs w:val="20"/></w:rPr><w:t>Availability</w:t></w:r></w:p></w:tc><w:sdt><w:sdtPr><w:rPr><w:sz w:val="20"/><w:szCs w:val="20"/></w:rPr><w:alias w:val="Low Moderate High"/><w:tag w:val="lowmoderatehigh"/><w:id w:val="829017778"/><w:placeholder><w:docPart w:val="4D57AA5107874C0EAE6342AA135AAA7E"/></w:placeholder><w:dropDownList><w:listItem w:value="Choose an item."/><w:listItem w:displayText="Low (L)" w:value="Low (L)"/><w:listItem w:displayText="Moderate (M)" w:value="Moderate (M)"/><w:listItem w:displayText="High (H)" w:value="High (H)"/></w:dropDownList></w:sdtPr><w:sdtEndPr/><w:sdtContent><w:tc><w:tcPr><w:tcW w:w="3633" w:type="pct"/><w:shd w:val="clear" w:color="auto" w:fill="auto"/><w:tcMar><w:top w:w="0" w:type="dxa"/><w:left w:w="101" w:type="dxa"/><w:bottom w:w="115" w:type="dxa"/><w:right w:w="101" w:type="dxa"/></w:tcMar></w:tcPr><w:p w14:paraId="12379D77" w14:textId="4BC04DBB" w:rsidR="00CC17A4" w:rsidRPr="00551FB0" w:rsidRDefault="00D87316" w:rsidP="00551FB0"><w:pPr><w:pStyle w:val="GSATableText"/><w:rPr><w:sz w:val="20"/><w:szCs w:val="20"/></w:rPr></w:pPr><w:r><w:rPr><w:sz w:val="20"/><w:szCs w:val="20"/></w:rPr><w:t>Moderate (M)</w:t></w:r></w:p></w:tc></w:sdtContent></w:sdt></w:tr></w:tbl>
|
|
131
|
+
"""
|
|
132
|
+
# Create a new document
|
|
133
|
+
root = etree.fromstring(xml_string)
|
|
134
|
+
table = Document().add_table(rows=1, cols=1)
|
|
135
|
+
table._tbl = root
|
|
136
|
+
# Return the document as a binary string
|
|
137
|
+
return table
|
|
138
|
+
|
|
139
|
+
@staticmethod
|
|
140
|
+
@pytest.fixture
|
|
141
|
+
def document():
|
|
142
|
+
"""Create a docx document"""
|
|
143
|
+
doc = Document()
|
|
144
|
+
doc.add_heading("Header 1", level=1)
|
|
145
|
+
doc.add_paragraph("Some text")
|
|
146
|
+
doc.add_paragraph("More text")
|
|
147
|
+
doc.add_heading("Header 2", level=1)
|
|
148
|
+
doc.add_paragraph("Some more text")
|
|
149
|
+
doc.add_paragraph("Even more text")
|
|
150
|
+
return doc
|
|
151
|
+
|
|
152
|
+
@pytest.fixture
|
|
153
|
+
def test_document(self):
|
|
154
|
+
"""Load test document"""
|
|
155
|
+
doc = Document(f"{self.get_tests_dir('tests')}/test_data/RegScale FedRAMP Mock Authorization Package.docx")
|
|
156
|
+
return doc
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def test_gen_key():
|
|
160
|
+
"""Test the gen_key function with various control ID patterns"""
|
|
161
|
+
|
|
162
|
+
# Test cases for basic control IDs without enhancements
|
|
163
|
+
assert gen_key("AC-1") == "AC-1"
|
|
164
|
+
assert gen_key("SI-2") == "SI-2"
|
|
165
|
+
assert gen_key("AU-3") == "AU-3"
|
|
166
|
+
|
|
167
|
+
# Test cases for control IDs with numeric enhancements
|
|
168
|
+
assert gen_key("AC-1(1)") == "AC-1(1)"
|
|
169
|
+
assert gen_key("SI-2(5)") == "SI-2(5)"
|
|
170
|
+
assert gen_key("AU-3(10)") == "AU-3(10)"
|
|
171
|
+
|
|
172
|
+
# Test cases for control IDs with letter enhancements (should be stripped)
|
|
173
|
+
assert gen_key("AC-1(a)") == "AC-1"
|
|
174
|
+
assert gen_key("SI-2(b)") == "SI-2"
|
|
175
|
+
assert gen_key("AU-3(z)") == "AU-3"
|
|
176
|
+
|
|
177
|
+
# Test cases for control IDs with both numeric and letter enhancements
|
|
178
|
+
assert gen_key("AC-1(1)(a)") == "AC-1(1)"
|
|
179
|
+
assert gen_key("SI-2(5)(b)") == "SI-2(5)"
|
|
180
|
+
assert gen_key("AU-3(10)(z)") == "AU-3(10)"
|
|
181
|
+
|
|
182
|
+
# Test cases for edge cases
|
|
183
|
+
assert gen_key("AC-1(1)(A)") == "AC-1(1)" # Uppercase letter
|
|
184
|
+
assert gen_key("SI-2(5)(B)") == "SI-2(5)" # Uppercase letter
|
|
185
|
+
assert gen_key("AU-3(10)(Z)") == "AU-3(10)" # Uppercase letter
|
|
186
|
+
|
|
187
|
+
# Test cases for malformed or non-matching patterns
|
|
188
|
+
assert gen_key("AC1") == "AC1" # No dash
|
|
189
|
+
assert gen_key("AC-1a") == "AC-1a" # Letter without parentheses
|
|
190
|
+
assert gen_key("AC-1(1a)") == "AC-1(1a)" # Mixed in parentheses
|
|
191
|
+
assert gen_key("") == "" # Empty string
|
|
192
|
+
assert gen_key("AC-1(b)") == "AC-1" # Multiple numeric enhancements (keeps first)
|
|
193
|
+
|
|
194
|
+
# Test cases for different control families
|
|
195
|
+
assert gen_key("CM-1") == "CM-1"
|
|
196
|
+
assert gen_key("IA-1") == "IA-1"
|
|
197
|
+
assert gen_key("MP-1") == "MP-1"
|
|
198
|
+
assert gen_key("PE-1") == "PE-1"
|
|
199
|
+
assert gen_key("PL-1") == "PL-1"
|
|
200
|
+
assert gen_key("PS-1") == "PS-1"
|
|
201
|
+
assert gen_key("RA-1") == "RA-1"
|
|
202
|
+
assert gen_key("SA-1") == "SA-1"
|
|
203
|
+
assert gen_key("SC-1") == "SC-1"
|
|
204
|
+
assert gen_key("SR-1") == "SR-1"
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
@pytest.mark.skip("Not implemented")
|
|
208
|
+
def test_validate_date_str():
|
|
209
|
+
# Test valid date
|
|
210
|
+
assert validate_date_str("05/05/2023")
|
|
211
|
+
# Test invalid date
|
|
212
|
+
assert validate_date_str("May 5, 2023") is False
|
|
213
|
+
# Test exception handling
|
|
214
|
+
mock_datetime = MagicMock()
|
|
215
|
+
mock_datetime.strptime.side_effect = ValueError()
|
|
216
|
+
with patch("datetime.datetime", mock_datetime):
|
|
217
|
+
assert validate_date_str("05/05/2023")
|
|
218
|
+
assert validate_date_str("marches 5 2021") is False
|
|
219
|
+
|
|
220
|
+
@pytest.mark.skip("Not implemented")
|
|
221
|
+
def test_check_profile(self):
|
|
222
|
+
# Create mock response data
|
|
223
|
+
api = self.api
|
|
224
|
+
data = [
|
|
225
|
+
{
|
|
226
|
+
"id": 576,
|
|
227
|
+
"uuid": "47bdf9eb-e885-40e6-b1f6-e7b906f231bf",
|
|
228
|
+
"name": "CDS_Access",
|
|
229
|
+
"confidentiality": "High",
|
|
230
|
+
"integrity": "High",
|
|
231
|
+
"availability": "Moderate",
|
|
232
|
+
"category": "High",
|
|
233
|
+
"profileOwner": "Eaton, Bryan",
|
|
234
|
+
"profileOwnerId": "62b632b9-20c0-45c8-87d9-edd34942c3ae",
|
|
235
|
+
"createdById": "62b632b9-20c0-45c8-87d9-edd34942c3ae",
|
|
236
|
+
"dateCreated": "2023-03-29T20:59:41.4556531",
|
|
237
|
+
"lastUpdatedById": "62b632b9-20c0-45c8-87d9-edd34942c3ae",
|
|
238
|
+
"dateLastUpdated": "2023-03-29T20:59:41.4557173",
|
|
239
|
+
"isPublic": True,
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
|
|
243
|
+
mock_response = create_mock_response(json_data=data)
|
|
244
|
+
# Mock the API object and its `get` method to return the mock response
|
|
245
|
+
api.get = MagicMock(return_value=mock_response)
|
|
246
|
+
# Define the input parameters
|
|
247
|
+
config = self.config
|
|
248
|
+
title = "CDS_Access"
|
|
249
|
+
|
|
250
|
+
# Call the `check_profile` function with the input parameters
|
|
251
|
+
result = check_profile(api, config, title)
|
|
252
|
+
|
|
253
|
+
# Assert that the function returns the correct profile ID
|
|
254
|
+
assert result == 576
|
|
255
|
+
|
|
256
|
+
@pytest.mark.skip("Not implemented")
|
|
257
|
+
def test_create_component_mapping(self, caplog):
|
|
258
|
+
# Create mock response data
|
|
259
|
+
api = self.api
|
|
260
|
+
data = {
|
|
261
|
+
"id": 614,
|
|
262
|
+
"uuid": "4176063c-ae0d-4727-a6df-e582b01d6d27",
|
|
263
|
+
"securityPlanId": 279,
|
|
264
|
+
"componentId": 676,
|
|
265
|
+
"createdById": "62b632b9-20c0-45c8-87d9-edd34942c3ae",
|
|
266
|
+
"dateCreated": "2023-05-04T17:30:17.2216596+00:00",
|
|
267
|
+
"lastUpdatedById": "62b632b9-20c0-45c8-87d9-edd34942c3ae",
|
|
268
|
+
"isPublic": True,
|
|
269
|
+
"dateLastUpdated": "2023-05-04T17:30:17.2216599+00:00",
|
|
270
|
+
"tenantsId": 1,
|
|
271
|
+
}
|
|
272
|
+
mock_response = create_mock_response(json_data=data)
|
|
273
|
+
|
|
274
|
+
# Mock the API object and its `post` method to return the mock response
|
|
275
|
+
api.post = MagicMock(return_value=mock_response)
|
|
276
|
+
|
|
277
|
+
# Define the input parameters
|
|
278
|
+
config = self.config
|
|
279
|
+
ssp_id = 279
|
|
280
|
+
cmp_id = 676
|
|
281
|
+
headers = {"Content-Type": self.content_type}
|
|
282
|
+
|
|
283
|
+
# Call the `create_component_mapping` function with the input parameters
|
|
284
|
+
create_component_mapping(api, config, ssp_id, cmp_id)
|
|
285
|
+
|
|
286
|
+
# Check that the `post` method was called with the correct URL and data
|
|
287
|
+
expected_data = {
|
|
288
|
+
"securityPlanId": ssp_id,
|
|
289
|
+
"componentId": cmp_id,
|
|
290
|
+
"isPublic": True,
|
|
291
|
+
"createdById": config["userId"],
|
|
292
|
+
"lastUpdatedById": config["userId"],
|
|
293
|
+
}
|
|
294
|
+
api.post.assert_called_once_with(
|
|
295
|
+
url=f"{config['domain']}/api/componentmapping",
|
|
296
|
+
headers=headers,
|
|
297
|
+
data=json.dumps(expected_data),
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
# Check that a warning message is logged if the status code is not 200
|
|
301
|
+
mock_response.status_code = 400
|
|
302
|
+
create_component_mapping(api, config, ssp_id, cmp_id)
|
|
303
|
+
assert "Unable to post Mapping Response" in caplog.records[-1].getMessage()
|
|
304
|
+
|
|
305
|
+
@pytest.mark.skip("Not implemented")
|
|
306
|
+
def test_create_port(self):
|
|
307
|
+
api = self.api
|
|
308
|
+
data = {
|
|
309
|
+
"parentId": 1016,
|
|
310
|
+
"parentModule": "components",
|
|
311
|
+
"startPort": 80,
|
|
312
|
+
"endPort": 80,
|
|
313
|
+
"protocol": "http",
|
|
314
|
+
"service": "",
|
|
315
|
+
"purpose": "service",
|
|
316
|
+
"used_by": "",
|
|
317
|
+
"createdById": None,
|
|
318
|
+
}
|
|
319
|
+
mock_response = create_mock_response(json_data=data)
|
|
320
|
+
|
|
321
|
+
# Mock the API object and its `post` method to return the mock response
|
|
322
|
+
api.post = MagicMock(return_value=mock_response)
|
|
323
|
+
|
|
324
|
+
# Define the input parameters
|
|
325
|
+
config = self.config
|
|
326
|
+
headers = {
|
|
327
|
+
"authorization": config["token"],
|
|
328
|
+
"accept": self.accept,
|
|
329
|
+
"Content-Type": self.content_type,
|
|
330
|
+
}
|
|
331
|
+
ports = PortsProtocol(**data)
|
|
332
|
+
create_port(api, config, ports)
|
|
333
|
+
|
|
334
|
+
api.post.assert_called_once_with(
|
|
335
|
+
url=f"{config['domain']}/api/portsProtocols",
|
|
336
|
+
headers=headers,
|
|
337
|
+
data=json.dumps(ports.__dict__),
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
assert api.post.call_count == 1
|
|
341
|
+
|
|
342
|
+
@pytest.mark.skip("Not implemented")
|
|
343
|
+
def test_create_ssp_components(self):
|
|
344
|
+
# Create mock response data
|
|
345
|
+
api = self.api
|
|
346
|
+
data = {
|
|
347
|
+
"id": 280,
|
|
348
|
+
"title": "This System",
|
|
349
|
+
"description": "The entire system as depicted in the system authorization boundary\\n\\nEmail is employed",
|
|
350
|
+
"componentType": "compliance artifact",
|
|
351
|
+
"componentOwnerId": "62b632b9-20c0-45c8-87d9-edd34942c3ae",
|
|
352
|
+
"purpose": None,
|
|
353
|
+
"securityPlansId": 280,
|
|
354
|
+
"cmmcAssetType": None,
|
|
355
|
+
"createdBy": None,
|
|
356
|
+
"createdById": "62b632b9-20c0-45c8-87d9-edd34942c3ae",
|
|
357
|
+
"dateCreated": None,
|
|
358
|
+
"lastUpdatedBy": None,
|
|
359
|
+
"lastUpdatedById": "62b632b9-20c0-45c8-87d9-edd34942c3ae",
|
|
360
|
+
"dateLastUpdated": None,
|
|
361
|
+
"status": "Active",
|
|
362
|
+
"uuid": None,
|
|
363
|
+
"componentOwner": None,
|
|
364
|
+
"cmmcExclusion": False,
|
|
365
|
+
"isPublic": True,
|
|
366
|
+
}
|
|
367
|
+
mock_response = create_mock_response(json_data=data)
|
|
368
|
+
api.post = MagicMock(return_value=mock_response)
|
|
369
|
+
|
|
370
|
+
config = self.config
|
|
371
|
+
components = [
|
|
372
|
+
{
|
|
373
|
+
"title": "Component 1",
|
|
374
|
+
"type": "Hardware",
|
|
375
|
+
"description": "This is a hardware component",
|
|
376
|
+
"status": {"state": "operational"},
|
|
377
|
+
"protocols": [
|
|
378
|
+
{
|
|
379
|
+
"name": "HTTP",
|
|
380
|
+
"port-ranges": [{"start": "80", "end": "80"}],
|
|
381
|
+
}
|
|
382
|
+
],
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"title": "Component 2",
|
|
386
|
+
"type": "Software",
|
|
387
|
+
"description": "This is a software component",
|
|
388
|
+
"status": {"state": "operational"},
|
|
389
|
+
},
|
|
390
|
+
]
|
|
391
|
+
ssp_id = 280
|
|
392
|
+
|
|
393
|
+
# Call the method
|
|
394
|
+
create_ssp_components(api, config, components, ssp_id)
|
|
395
|
+
|
|
396
|
+
# Check that the API was called with the expected parameters
|
|
397
|
+
assert api.post.call_count == 5
|
|
398
|
+
|
|
399
|
+
# Check the parameters of the first API call (creating Component 1)
|
|
400
|
+
expected_comp1 = Component(
|
|
401
|
+
title="Component 1",
|
|
402
|
+
securityPlansId=ssp_id,
|
|
403
|
+
componentType="Hardware",
|
|
404
|
+
lastUpdatedById=config["userId"],
|
|
405
|
+
createdById=config["userId"],
|
|
406
|
+
cmmcExclusion=False,
|
|
407
|
+
componentOwnerId=config["userId"],
|
|
408
|
+
description="This is a hardware component",
|
|
409
|
+
status="Active",
|
|
410
|
+
).dict()
|
|
411
|
+
expected_headers = {
|
|
412
|
+
"authorization": config["token"],
|
|
413
|
+
"accept": self.accept,
|
|
414
|
+
"Content-Type": self.content_type,
|
|
415
|
+
}
|
|
416
|
+
api.post.assert_any_call(
|
|
417
|
+
url=config["domain"] + "/api/components",
|
|
418
|
+
headers=expected_headers,
|
|
419
|
+
data=json.dumps(expected_comp1),
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
# Check the parameters of the second API call (creating Component 2)
|
|
423
|
+
expected_comp2 = Component(
|
|
424
|
+
title="Component 2",
|
|
425
|
+
securityPlansId=ssp_id,
|
|
426
|
+
componentType="Software",
|
|
427
|
+
lastUpdatedById=config["userId"],
|
|
428
|
+
createdById=config["userId"],
|
|
429
|
+
cmmcExclusion=False,
|
|
430
|
+
componentOwnerId=config["userId"],
|
|
431
|
+
description="This is a software component",
|
|
432
|
+
status="Active",
|
|
433
|
+
).dict()
|
|
434
|
+
api.post.assert_any_call(
|
|
435
|
+
url=config["domain"] + "/api/components",
|
|
436
|
+
headers=expected_headers,
|
|
437
|
+
data=json.dumps(expected_comp2),
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
@staticmethod
|
|
441
|
+
@pytest.mark.skip("Not implemented")
|
|
442
|
+
def test_get_tables(test_document):
|
|
443
|
+
# Create a mock document object with two tables
|
|
444
|
+
|
|
445
|
+
# Call the function
|
|
446
|
+
tables = get_tables(test_document)
|
|
447
|
+
|
|
448
|
+
# Check the results
|
|
449
|
+
assert len(tables) == 700
|
|
450
|
+
|
|
451
|
+
@pytest.mark.skip("Not implemented")
|
|
452
|
+
def test_post_regscale_object(self):
|
|
453
|
+
config = self.config
|
|
454
|
+
api = self.api
|
|
455
|
+
obj = {"id": 1, "name": "test_obj"}
|
|
456
|
+
endpoint = "controlimplementation"
|
|
457
|
+
data = {
|
|
458
|
+
"id": 1385,
|
|
459
|
+
"lastUpdatedById": "62b632b9-20c0-45c8-8",
|
|
460
|
+
"isPublic": False,
|
|
461
|
+
"url": "https://www.whitehouse.com",
|
|
462
|
+
"title": "Safeguarding Against whatevertion (PII)",
|
|
463
|
+
"parentID": 282,
|
|
464
|
+
"parentModule": "securityplans",
|
|
465
|
+
}
|
|
466
|
+
mock_response = create_mock_response(json_data=data)
|
|
467
|
+
api.post = MagicMock(return_value=mock_response)
|
|
468
|
+
post_regscale_object(api, config, obj, endpoint)
|
|
469
|
+
|
|
470
|
+
# Verify that api.post() was called with the correct arguments
|
|
471
|
+
api.post.assert_called_once_with(
|
|
472
|
+
config["domain"] + f"/api/{endpoint}",
|
|
473
|
+
headers={
|
|
474
|
+
"authorization": config["token"],
|
|
475
|
+
"accept": self.accept,
|
|
476
|
+
"Content-Type": self.content_type,
|
|
477
|
+
},
|
|
478
|
+
data='{"id": 1, "name": "test_obj"}',
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
@staticmethod
|
|
482
|
+
@pytest.mark.skip("Not implemented")
|
|
483
|
+
def test_get_xpath_data_detailed(example_table):
|
|
484
|
+
key = "Security Objective"
|
|
485
|
+
ident = "Confidentiality"
|
|
486
|
+
xpath = "//w:r/w:t"
|
|
487
|
+
count_array = [2, 3, 4]
|
|
488
|
+
tables = [example_table]
|
|
489
|
+
result = get_xpath_data_detailed(tables, key, ident, xpath, count_array)
|
|
490
|
+
assert result["type"] == "Security Objective"
|
|
491
|
+
assert result["nist_ident"] == "Confidentiality"
|
|
492
|
+
assert result["confidentiality"] is None
|
|
493
|
+
assert result["integrity"] is None
|
|
494
|
+
assert result["availability"] is None
|
|
495
|
+
|
|
496
|
+
@pytest.mark.skip("Not implemented")
|
|
497
|
+
def test_post_links(self):
|
|
498
|
+
api = self.api
|
|
499
|
+
config = self.config
|
|
500
|
+
document = MagicMock()
|
|
501
|
+
file_path: Path = Path("test_file_path.docx")
|
|
502
|
+
regscale_ssp = {"id": 407}
|
|
503
|
+
data = [{"title": "example link", "link": "www.google.com"}]
|
|
504
|
+
mock_response = create_mock_response(json_data=data)
|
|
505
|
+
api.get = MagicMock(return_value=mock_response)
|
|
506
|
+
post_links(config, api, document, file_path, regscale_ssp, post_embeds=False)
|
|
507
|
+
|
|
508
|
+
# Make assertions here
|
|
509
|
+
api.get.assert_called_once_with(
|
|
510
|
+
config["domain"] + "/api/links/getAllByParent/407/securityplans",
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
@staticmethod
|
|
514
|
+
@pytest.mark.skip("Not implemented")
|
|
515
|
+
def test_get_text(test_document):
|
|
516
|
+
document = test_document
|
|
517
|
+
full_text = []
|
|
518
|
+
[full_text.append(para.text) for para in document.paragraphs]
|
|
519
|
+
start_header = "Information System Components and Boundaries"
|
|
520
|
+
end_header = "Types of Users"
|
|
521
|
+
expected_output = "RegScale leverages Microsoft Azure commercial services to provide a Software and SaaS (software as a Service) Figure 9-1 "
|
|
522
|
+
assert get_text_between_headers(full_text, start_header, end_header) == expected_output
|
|
523
|
+
|
|
524
|
+
@staticmethod
|
|
525
|
+
@pytest.mark.skip("Not implemented")
|
|
526
|
+
def test_base_contact(test_document):
|
|
527
|
+
expected_output = {
|
|
528
|
+
"name": "",
|
|
529
|
+
"title": "",
|
|
530
|
+
"company": "",
|
|
531
|
+
"address": "",
|
|
532
|
+
"phone": "",
|
|
533
|
+
"email": "",
|
|
534
|
+
}
|
|
535
|
+
assert get_base_contact(test_document) == expected_output
|
|
536
|
+
|
|
537
|
+
@staticmethod
|
|
538
|
+
def test_implementation_status_fully_implemented(element, implementation):
|
|
539
|
+
element.set("name", "implementation-status")
|
|
540
|
+
element.set("value", "implemented")
|
|
541
|
+
ControlImplementation._update_implementation_status(element, implementation)
|
|
542
|
+
assert implementation.status == ControlImplementationStatus.FullyImplemented
|
|
543
|
+
assert implementation.bStatusImplemented is True
|
|
544
|
+
|
|
545
|
+
@staticmethod
|
|
546
|
+
def test_implementation_status_invalid(element, implementation):
|
|
547
|
+
element.set("name", "implementation-status")
|
|
548
|
+
element.set("value", "unknown-status")
|
|
549
|
+
ControlImplementation._update_implementation_status(element, implementation)
|
|
550
|
+
assert implementation.status is None
|
|
551
|
+
|
|
552
|
+
@staticmethod
|
|
553
|
+
def test_control_origination_provider(element, implementation):
|
|
554
|
+
element.set("name", "control-origination")
|
|
555
|
+
element.set("value", "sp-corporate")
|
|
556
|
+
ControlImplementation._update_implementation_status(element, implementation)
|
|
557
|
+
assert implementation.responsibility == ControlImplementationOrigin.Provider
|
|
558
|
+
assert implementation.bServiceProviderCorporate is True
|
|
559
|
+
|
|
560
|
+
@staticmethod
|
|
561
|
+
def test_control_origination_invalid(element, implementation):
|
|
562
|
+
element.set("name", "control-origination")
|
|
563
|
+
element.set("value", "invalid-origin")
|
|
564
|
+
ControlImplementation._update_implementation_status(element, implementation)
|
|
565
|
+
assert implementation.responsibility is None
|
|
566
|
+
|
|
567
|
+
@staticmethod
|
|
568
|
+
def test_extract_parts():
|
|
569
|
+
# test case for the extract_parts function
|
|
570
|
+
# test case 1
|
|
571
|
+
# when the input is a string
|
|
572
|
+
# the function should return a Dict of the individual parts
|
|
573
|
+
test_dict = {
|
|
574
|
+
"T-1": "<p>Part a:</p><p>The FedRAMP Compliance Director is responsible for developing,documenting, and disseminating system-level security policies andprocedures, as well as ensuring that the proper NIST SP 800-53, Revision5, high controls are implemented to protect the confidentiality,integrity, and availability of the CC2CGS information system and itsdata. All revisions to policies and procedures are reviewed and approvedby the FedRAMP Compliance Director. Upon final approval, policies andprocedures are published to Amazon S3 and made available to employees onan as-needed basis.</p><p>The access control policy is comprised of the following sections:</p><ul><li><p>Introduction</p></li><li><p>Purpose</p></li><li><p>Scope</p></li><li><p>Roles and Responsibilities</p></li><li><p>Management Commitment</p></li><li><p>Authority</p></li><li><p>Document Control</p></li><li><p>Compliance</p></li><li><p>Policy Requirements</p></li></ul><p>The access control policy is consistent with:</p><ul><li><p>Applicable laws</p></li><li><p>Executive orders</p></li><li><p>Directives</p></li><li><p>Regulations</p></li><li><p>Policies</p></li><li><p>Standards</p></li><li><p>Guidelines</p></li></ul><p>The system-level policy provides the security framework upon whichall access control efforts will be based. The provisions of the policyand procedures pertain to all Test Case employees, contingent workers,contractors, business partners, and third parties accessing all CC2CGScomponents that process, transmit, and store government information.This includes information held on behalf of government customers,partners, and other third parties, to protect the confidentiality,integrity, and availability of Test Case confidential information andsystems. Operational systems and systems under development must meetaccess control requirements commensurate with the sensitivity of theinformation they handle and the potential impact of unauthorizeddisclosure of information and data.</p><p>Part b:</p><p>Test Case has designated the FedRAMP Compliance Director to managethe development, documentation, and dissemination of the access controlpolicy and procedures.</p><p>Part c:</p><p>The FedRAMP Compliance Director reviews and updates the accesscontrol policy and procedures at least annually. Policy and procedurerevisions may also be carried out when required due to significantchanges in the environment.</p>",
|
|
575
|
+
"T-2": "<p>Part a:</p><p><strong><u>Test Case Responsibility</u></strong></p><p>Test Case has identified the following types of information systemaccounts that support its organizational mission and business functionsrelated to the CC2CGS federal system:</p><ul><li><p>Microsoft Entra ID/ Microsoft Entra ID accounts – Test Case usesMicrosoft Entra ID as a Security Assertion Markup Language (SAML) andMFA provider to centrally control access to the CC2CGS federalproduction environment. All Test Case team members who administer andmanage the production environment are provisioned Microsoft Entra IDaccounts, which serve as an SSO mechanism for backend access via AmazonWorkspaces to the production environment. Access to the CC2CGS requirespersonnel to have Microsoft Entra ID credentials, as well as FIPS 5CYubiKey Federal MFA. Authorization and access are determined during theaccount provisioning process. Personnel are given access based on theirroles and responsibilities in accordance with separation of duties andleast privileged principles.</p></li><li><p>AWS root/ IAM accounts – The AWS root account for the CC2CGSenvironment is managed by the SRE Team and is not used for day-to-dayoperations. Instead, IAM accounts are used by authorized personnel andare separated using groups and roles based on their jobfunctions.</p></li><li><p>Security tools and applications – The SRE Team access third-partytools such as Trend Micro Deep Security Manager (DSM) and SplunkEnterprise with Enterprise Security using a secured HTTPS web connectionand a combination of username and password. These accounts are managedby the SRE Team.</p></li><li><p>Initial customer account: After a new customer signs a contract,SRE Team creates the initial domain administrator account and emails thecustomer administrator with a link to the account. The customer domainadministrator is responsible for setting up the rest of theirorganization's accounts.</p></li></ul><p><strong><u>Federal Customer Responsibility</u></strong></p><p>Once the initial administrator account is set up by Test Case,customers are responsible for establishing conditions for rolemembership for their CC2CGS accounts.</p><p>Part b:</p><p><strong><u>Test Case Responsibility</u></strong></p><p>Test Case assigns account managers for all internal informationsystem accounts. The SRE Team and Customer Support Team own andadminister the Microsoft Entra ID, AWS IAM accounts, and security tools.The SRE Team is considered the owner of the AWS root account. The SRETeam and Customer Support Team set up the initial Customer Administratoraccount.</p><p><strong>Federal Customer Responsibility</strong></p><p>Customers are responsible for designating an account owner to beprovided the Customer Administrator account for their access to CC2CGS.After the initial Customer Administrator account is created byTest Case, the customer administrator is responsible for creating theCC2CGS accounts for the individual users within theirorganization.</p><p>Part c:</p><p><strong>Test Case Responsibility</strong></p><p>Access requests are submitted via Jira Service Management. All accessto CC2CGS (both the Application and the Management plane) is assignedusing role-based access control (RBAC). A user's role determines theirassigned permissions, privileges, and access within the CC2CGSenvironment. Users must complete prerequisites based on their role, suchas signed access agreements, completion of applicable trainingscompleting incident response training, or signing the rules of behavior(RoB), prior to being granted access to CC2CGS</p><p><strong><u>Federal Customer Responsibility</u></strong></p><p>Customers are responsible for establishing conditions for rolemembership for their CC2CGS user accounts.</p><p>Part d:</p><p><strong><u>Test Case Responsibility</u></strong></p><p>Test Case has developed an access request process for approval andcreation of all internal CC2CGS accounts. Each request must be based ona business need, and access is limited to the access required for theindividual to perform their identified role within the system. Allrequests for account creation, modification, and disabling areinitiated, approved, and tracked in a Jira Service Management ticket.All users are assigned an individual account to access theenvironment.</p><p><strong><u>Federal Customer Responsibility</u></strong></p><p>Once Test Case creates the initial Customer Administrator account inCC2CGS, the Customer Administrator is responsible for creating andmanaging CC2CGS accounts for the individual users within theirorganization and for developing a process to identify users who requireaccess to the system and their associated levels ofpermissions.</p><p>Part e:</p><p><strong><u>Test Case Responsibility</u></strong></p><p>All new Test Case employees are provided with basic access to email,corporate information technology (IT) tools, and other Test Caseresources, as part of the initial onboarding process. For personnel whorequire access to the CC2CGS production environment, SRE Team andCustomer Support Team must submit a Jira Service Management ticket witha workflow is used to obtain authorization from the employee'ssupervisor and the account owner.</p><p>Federal Customers are onboarded by SRE Team and Customer Support Teampersonnel only after the customer has signed a contract. The SRE Teamand the Customer Support Team create the initial Customer Administratoraccount with a randomly generated password and email the CustomerAdministrator with a link to their account. The Customer Administratormust change the initial password upon first login. Accounts are approvedand signed off by the SRE Team and Customer Support Team.</p><p><strong><u>Federal Customer Responsibility</u></strong></p><p>After the initial Customer Administrator account is created byTest Case, the Customer Administrator is responsible for creating theCC2CGS accounts for the individual users within their organization,vendors, and partners and for ensuring that all access isapproved.</p><p>Part f:</p><p><strong><u>Test Case Responsibility</u></strong></p><p>For Test Case personnel who require access to the CC2CGS productionenvironment, a standard access request process is used, in which a JiraService Management ticket with a workflow is used to obtainauthorization from the employee's supervisor and the account owner.Ticket information specifies the user, identifier, role(s) requested,and approver. Each request must be based on a business need, and accessis limited to the access required for that individual to perform theiridentified role within the system.</p><p>When an existing employee is terminated or leaves the company, thereporting manager notifies HR, and HR begins the off-boarding process inthe corporate HR management system. A notification is sent to IT Teamwho then disables all accounts on the date indicated in the off-boardingticket.</p><p>Federal Customers are onboarded by the SRE Team and Customer SupportTeam only after the customer has signed a contract. The SRE Team andCustomer Support Team create an initial customer administrator accountin CC2CGS and email the user ID and initial password to the customer.Accounts are created in accordance with the CC2CGS access control policyand procedure.</p><p>If an update needs to be made to an account, a request is submittedto the SRE Team and Customer Support Team through Jira ServiceManagement. The SRE Team and Customer Support Team review the requestand update the account if approved. If there is an update in the user'sMicrosoft Entra ID, the SRE Team and Customer Support Team ensures theupdate is reflected within the account as necessary.</p><p><strong><u>Federal Customer Responsibility</u></strong></p><p>After the initial Customer Administrator account is created byTest Case, the Customer Administrator is responsible for creating theCC2CGS accounts for the individual users and for modifying, disabling,and removing those accounts in accordance with their organizationalpolicies and procedures.</p><p>Part g:</p><p><strong><u>Test Case Responsibility</u></strong></p><p>Logging and monitoring of account activity in the CC2CGS environmentis performed using Splunk Enterprise Security. Alerts are set up inSplunk Enterprise Security for specific unauthorized actions, andnotifications are sent to on-call personnel if threats are detected.</p><p><strong><u>Federal Customer Responsibility</u></strong></p><p>After the initial Customer Administrator account is created byTest Case, the Customer Administrator is responsible for creating theCC2CGS accounts for the individual users and for monitoring the useraccounts they create within CC2CGS.</p><p>Part h:</p><p><strong>Test Case Responsibility</strong></p><p>When an existing employee is terminated, transferred, or leavesTest Case, the reporting manager notifies HR within 24 hours, and HRbegins the off-boarding process. A notification is also sent to the SRETeam and the Customer Support Team, who then disables all accountswithin eight (8) hours in the off-boarding ticket. All access isdisabled on the employee's eight (8) hours as a part of Test Case or ifthey transfer to another role within Test Case. Additionally,notifications are sent to SRE Team, Customer Support Team, FedRAMPCompliance Director when an account or role is no longer required withinCC2CGS.</p><p><strong>Federal Customer Responsibility</strong></p><p>Customer account owners are responsible for implementing processes toensure that they are notified when user access is no longer required,when a user is transferred or terminated, or when an individual'sneed-to-know changes.</p><p>Part i:</p><p><strong>Test Case Responsibility</strong></p><p>All CC2CGS Management plane personnel are granted a Microsoft EntraID account and a FIPS 5C YubiKey Federal MFA account. All accountrequests are submitted through the Test Case access request form andtracked via the Jira Service Management ticketing system. Account accessis based on user roles and responsibilities and must be approved by theSRE Team and the Customer Support Team.</p><p><strong>Federal Customer Responsibility</strong></p><p>Customers are responsible for authorizing access to their CC2CGSaccounts based on valid access authorization, intended system usage, andother attributes defined by the customer organization.</p><p>Part j:</p><p><strong><u>Test Case Responsibility</u></strong></p><p>Test Case assigns account managers for all internal informationsystem accounts. The SRE Team and Customer Support Team own andadminister the Microsoft Entra ID, AWS IAM accounts, and security tools.The SRE Team and Customer Support Team set up the initial CustomerAdministrator account. The FedRAMP Compliance Director performs accountreviews at least monthly for privileged access and every six (6) monthsfor non-privileged access.</p><p><strong>Federal Customer Responsibility</strong></p><p>Customers are responsible for designating an account owner to beprovided with the Customer Administrator account for their access to theCC2CGS system. After the initial Customer Administrator account iscreated by Test Case, the customer administrator is responsible forcreating the CC2CGS accounts for the individual users within theirorganization and performs account reviews at least monthly forprivileged access and every six (6) months for non-privilegedaccess.</p><p>Part k:</p><p><strong>Test Case Responsibility</strong></p><p>Shared credentials are not used within CC2CGS. The SRE Team isconsidered the owner of the AWS root account and Test Case prohibitsthe user of shared or group accounts through the access control policy.All users must have unique credentials to access the CC2CGS productionenvironment.</p><p><strong><u>Federal Customer Responsibility</u></strong></p><p>Customers are responsible for determining whether shared or groupaccounts will be used and for determining a process for reissuing sharedor group account credentials when individuals are removed from thegroup, if applicable.</p><p>Part l:</p><p><strong><u>Test Case Responsibility</u></strong></p><p>Test Case integrates the personnel termination and transferprocesses with the account management process for CC2CGS. Forterminations and transfers, HR begins the process to have access removedor updated by SRE Team and Customer Support Team by submitting a JiraService Management ticket.</p><p><strong><u>Federal Customer Responsibility</u></strong></p><p>Customers are responsible for aligning account management processeswith personnel termination and transfer processes.</p>",
|
|
576
|
+
"T-3": "test one part or default value for one part",
|
|
577
|
+
}
|
|
578
|
+
test_1 = test_dict.get("T-1")
|
|
579
|
+
test_2 = test_dict.get("T-2")
|
|
580
|
+
test_3 = test_dict.get("T-3")
|
|
581
|
+
dict_test_1 = extract_parts(test_1)
|
|
582
|
+
dict_test_2 = extract_parts(test_2)
|
|
583
|
+
dict_test_3 = extract_parts(test_3)
|
|
584
|
+
# test case 1
|
|
585
|
+
expected_t1_keys = ["Part a", "Part b", "Part c"]
|
|
586
|
+
assert list(dict_test_1.keys()) == expected_t1_keys
|
|
587
|
+
|
|
588
|
+
# test case 2
|
|
589
|
+
# assert each key in dict_test_2 is equal to the expected value
|
|
590
|
+
expected_t2_keys = [
|
|
591
|
+
"Part a",
|
|
592
|
+
"Part b",
|
|
593
|
+
"Part c",
|
|
594
|
+
"Part d",
|
|
595
|
+
"Part e",
|
|
596
|
+
"Part f",
|
|
597
|
+
"Part g",
|
|
598
|
+
"Part h",
|
|
599
|
+
"Part i",
|
|
600
|
+
"Part j",
|
|
601
|
+
"Part k",
|
|
602
|
+
"Part l",
|
|
603
|
+
]
|
|
604
|
+
assert list(dict_test_2.keys()) == expected_t2_keys
|
|
605
|
+
|
|
606
|
+
# test case 3
|
|
607
|
+
assert dict_test_3 == {"default": "test one part or default value for one part"}
|
|
608
|
+
|
|
609
|
+
def test_parse_ips(self):
|
|
610
|
+
"""
|
|
611
|
+
Test parsing v6 and v4 IP addresses
|
|
612
|
+
"""
|
|
613
|
+
test_ips: list[dict] = [
|
|
614
|
+
{
|
|
615
|
+
"ip": "23.45.55.55",
|
|
616
|
+
"asset_field": "ipAddress",
|
|
617
|
+
"valid_ip": True,
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
"ip": "10.25.255.191",
|
|
621
|
+
"asset_field": "ipAddress",
|
|
622
|
+
"valid_ip": True,
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
"ip": "23.45.265.254",
|
|
626
|
+
"asset_field": "ipAddress",
|
|
627
|
+
"valid_ip": False,
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
"ip": "45,34,54,56",
|
|
631
|
+
"asset_field": "ipAddress",
|
|
632
|
+
"valid_ip": False,
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
"ip": "0.42.34.76",
|
|
636
|
+
"asset_field": "ipAddress",
|
|
637
|
+
"valid_ip": False,
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
"ip": "2",
|
|
641
|
+
"asset_field": "ipAddress",
|
|
642
|
+
"valid_ip": False,
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
"ip": "1.2.3",
|
|
646
|
+
"asset_field": "ipAddress",
|
|
647
|
+
"valid_ip": False,
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
"ip": "255.256.267.300",
|
|
651
|
+
"asset_field": "ipAddress",
|
|
652
|
+
"valid_ip": False,
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
"ip": "2001:db8:3333:4444:5555:6666:7777:8888",
|
|
656
|
+
"asset_field": "iPv6Address",
|
|
657
|
+
"valid_ip": True,
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
"ip": "1050:0000:0000:0000:0005:0600:300c:326b",
|
|
661
|
+
"asset_field": "iPv6Address",
|
|
662
|
+
"valid_ip": True,
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
"ip": "1050:0:0:0:5:600:300c:326b",
|
|
666
|
+
"asset_field": "iPv6Address",
|
|
667
|
+
"valid_ip": True,
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
"ip": "2001:db8::",
|
|
671
|
+
"asset_field": "iPv6Address",
|
|
672
|
+
"valid_ip": True,
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
"ip": "2001:db8::1234:5678",
|
|
676
|
+
"asset_field": "iPv6Address",
|
|
677
|
+
"valid_ip": True,
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
"ip": "::1234:5678",
|
|
681
|
+
"asset_field": "iPv6Address",
|
|
682
|
+
"valid_ip": True,
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
"ip": "2001:db8:a0b:12f0::::0:1",
|
|
686
|
+
"asset_field": "iPv6Address",
|
|
687
|
+
"valid_ip": False,
|
|
688
|
+
},
|
|
689
|
+
{
|
|
690
|
+
"ip": "::ffff:192.0.2.128",
|
|
691
|
+
"asset_field": "iPv6Address",
|
|
692
|
+
"valid_ip": True,
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
"ip": "2001:0db8:85a3:0000:0000:8a2e:0370:zzzz",
|
|
696
|
+
"asset_field": "iPv6Address",
|
|
697
|
+
"valid_ip": False,
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
"ip": "2001:db8:85a3::8a2e:370g:7334",
|
|
701
|
+
"asset_field": "iPv6Address",
|
|
702
|
+
"valid_ip": False,
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
"ip": "2001::85a3::8a2e:370:7334",
|
|
706
|
+
"asset_field": "iPv6Address",
|
|
707
|
+
"valid_ip": False,
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
"ip": "12345::85a3:8a2e:0370:7334",
|
|
711
|
+
"asset_field": "iPv6Address",
|
|
712
|
+
"valid_ip": False,
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
"ip": "::ffff:192.0.2.256",
|
|
716
|
+
"asset_field": "iPv6Address",
|
|
717
|
+
"valid_ip": False,
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
"ip": "2001:0db8:85a3::8a2e::7334",
|
|
721
|
+
"asset_field": "iPv6Address",
|
|
722
|
+
"valid_ip": False,
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
"ip": "::",
|
|
726
|
+
"asset_field": "iPv6Address",
|
|
727
|
+
"valid_ip": True,
|
|
728
|
+
},
|
|
729
|
+
{
|
|
730
|
+
"ip": "2001:db8:85a3:8d3:1319:8a2e:370:7348",
|
|
731
|
+
"asset_field": "iPv6Address",
|
|
732
|
+
"valid_ip": True,
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
"ip": "2001:db8:85a3:8d3:1319:8a2e:370g:7348",
|
|
736
|
+
"asset_field": "iPv6Address",
|
|
737
|
+
"valid_ip": False,
|
|
738
|
+
},
|
|
739
|
+
{
|
|
740
|
+
"ip": "abcd:ef01:2345:6789:abcd:ef01:2345:6789",
|
|
741
|
+
"asset_field": "iPv6Address",
|
|
742
|
+
"valid_ip": True,
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
"ip": "2001:db8:1234:5678:abcd:ef01:2345:12345",
|
|
746
|
+
"asset_field": "iPv6Address",
|
|
747
|
+
"valid_ip": False,
|
|
748
|
+
},
|
|
749
|
+
{
|
|
750
|
+
"ip": "1200::AB00:1234::2552:7777:1313",
|
|
751
|
+
"asset_field": "iPv6Address",
|
|
752
|
+
"valid_ip": False,
|
|
753
|
+
},
|
|
754
|
+
]
|
|
755
|
+
|
|
756
|
+
for test in test_ips:
|
|
757
|
+
self.logger.info(f"Testing IP: {test['ip']}")
|
|
758
|
+
asset_field = determine_ip_address_version(test["ip"])
|
|
759
|
+
self.logger.info(f"Asset Field: {asset_field}")
|
|
760
|
+
if test["valid_ip"]:
|
|
761
|
+
assert asset_field == test["asset_field"]
|
|
762
|
+
else:
|
|
763
|
+
assert asset_field is None
|
|
764
|
+
|
|
765
|
+
@staticmethod
|
|
766
|
+
def test_update_ip_addresses_ipv4():
|
|
767
|
+
asset = {}
|
|
768
|
+
inventory = {"IPV4_OR_IPV6_ADDRESS": "192.168.1.1"}
|
|
769
|
+
mapping = MagicMock()
|
|
770
|
+
mapping.get_value.return_value = "192.168.1.1"
|
|
771
|
+
|
|
772
|
+
_update_ip_addresses(asset, inventory, mapping)
|
|
773
|
+
|
|
774
|
+
assert asset["ipAddress"] == "192.168.1.1"
|
|
775
|
+
assert asset["iPv6Address"] is None
|
|
776
|
+
|
|
777
|
+
@staticmethod
|
|
778
|
+
def test_update_ip_addresses_ipv6():
|
|
779
|
+
asset = {}
|
|
780
|
+
inventory = {"IPV4_OR_IPV6_ADDRESS": "2001:db8:3333:4444:5555:6666:7777:8888"}
|
|
781
|
+
mapping = MagicMock()
|
|
782
|
+
mapping.get_value.return_value = "2001:db8:3333:4444:5555:6666:7777:8888"
|
|
783
|
+
|
|
784
|
+
_update_ip_addresses(asset, inventory, mapping)
|
|
785
|
+
|
|
786
|
+
assert asset["ipAddress"] is None
|
|
787
|
+
assert asset["iPv6Address"] == "2001:db8:3333:4444:5555:6666:7777:8888"
|
|
788
|
+
|
|
789
|
+
@staticmethod
|
|
790
|
+
def test_update_ip_addresses_multiple_addresses():
|
|
791
|
+
asset = {}
|
|
792
|
+
inventory = {"IPV4_OR_IPV6_ADDRESS": "192.168.1.1, 2001:db8:3333:4444:5555:6666:7777:8888"}
|
|
793
|
+
mapping = MagicMock()
|
|
794
|
+
mapping.get_value.return_value = "192.168.1.1, 2001:db8:3333:4444:5555:6666:7777:8888"
|
|
795
|
+
|
|
796
|
+
_update_ip_addresses(asset, inventory, mapping)
|
|
797
|
+
|
|
798
|
+
assert asset["ipAddress"] == "192.168.1.1"
|
|
799
|
+
assert asset["iPv6Address"] == "2001:db8:3333:4444:5555:6666:7777:8888"
|
|
800
|
+
|
|
801
|
+
@staticmethod
|
|
802
|
+
def test_update_ip_addresses_multiple_addresses_with_semicolons():
|
|
803
|
+
asset = {}
|
|
804
|
+
inventory = {"IPV4_OR_IPV6_ADDRESS": "192.168.1.1; 2001:db8:3333:4444:5555:6666:7777:8888"}
|
|
805
|
+
mapping = MagicMock()
|
|
806
|
+
mapping.get_value.return_value = "192.168.1.1; 2001:db8:3333:4444:5555:6666:7777:8888"
|
|
807
|
+
|
|
808
|
+
_update_ip_addresses(asset, inventory, mapping)
|
|
809
|
+
|
|
810
|
+
assert asset["ipAddress"] == "192.168.1.1"
|
|
811
|
+
assert asset["iPv6Address"] == "2001:db8:3333:4444:5555:6666:7777:8888"
|
|
812
|
+
|
|
813
|
+
@staticmethod
|
|
814
|
+
def test_update_ip_addresses_multiple_addresses_with_commas_and_semicolons():
|
|
815
|
+
asset = {}
|
|
816
|
+
inventory = {
|
|
817
|
+
"IPV4_OR_IPV6_ADDRESS": "192.168.1.1, 2001:db8:3333:4444:5555:6666:7777:8888; 192.168.1.2; 2001:db8:3333:4444:5555:6666:7777:8888"
|
|
818
|
+
}
|
|
819
|
+
mapping = MagicMock()
|
|
820
|
+
mapping.get_value.return_value = (
|
|
821
|
+
"192.168.1.1, 2001:db8:3333:4444:5555:6666:7777:8888; 192.168.1.2; 2001:db8:3333:4444:5555:6666:7777:8888"
|
|
822
|
+
)
|
|
823
|
+
|
|
824
|
+
_update_ip_addresses(asset, inventory, mapping)
|
|
825
|
+
|
|
826
|
+
assert asset["ipAddress"] == "192.168.1.1, 192.168.1.2"
|
|
827
|
+
assert asset["iPv6Address"] == "2001:db8:3333:4444:5555:6666:7777:8888, 2001:db8:3333:4444:5555:6666:7777:8888"
|
|
828
|
+
|
|
829
|
+
@staticmethod
|
|
830
|
+
def test_multiple_ip_addresses_with_newlines():
|
|
831
|
+
asset = {}
|
|
832
|
+
inventory = {"IPV4_OR_IPV6_ADDRESS": "192.168.1.1\n2001:db8:3333:4444:5555:6666:7777:8888"}
|
|
833
|
+
mapping = MagicMock()
|
|
834
|
+
mapping.get_value.return_value = "192.168.1.1\n2001:db8:3333:4444:5555:6666:7777:8888"
|
|
835
|
+
|
|
836
|
+
_update_ip_addresses(asset, inventory, mapping)
|
|
837
|
+
|
|
838
|
+
assert asset["ipAddress"] == "192.168.1.1"
|
|
839
|
+
assert asset["iPv6Address"] == "2001:db8:3333:4444:5555:6666:7777:8888"
|
|
840
|
+
|
|
841
|
+
@staticmethod
|
|
842
|
+
def test_update_ip_addresses_empty():
|
|
843
|
+
asset = {}
|
|
844
|
+
inventory = {"IPV4_OR_IPV6_ADDRESS": ""}
|
|
845
|
+
mapping = MagicMock()
|
|
846
|
+
mapping.get_value.return_value = ""
|
|
847
|
+
|
|
848
|
+
_update_ip_addresses(asset, inventory, mapping)
|
|
849
|
+
|
|
850
|
+
assert asset.get("ipAddress") is None
|
|
851
|
+
assert asset.get("iPv6Address") is None
|