regscale-cli 6.23.0.0__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.

Files changed (44) hide show
  1. regscale/_version.py +1 -1
  2. regscale/core/app/application.py +2 -0
  3. regscale/integrations/commercial/__init__.py +1 -0
  4. regscale/integrations/commercial/sarif/sarif_converter.py +1 -1
  5. regscale/integrations/commercial/wizv2/click.py +109 -2
  6. regscale/integrations/commercial/wizv2/compliance_report.py +1485 -0
  7. regscale/integrations/commercial/wizv2/constants.py +72 -2
  8. regscale/integrations/commercial/wizv2/data_fetcher.py +61 -0
  9. regscale/integrations/commercial/wizv2/file_cleanup.py +104 -0
  10. regscale/integrations/commercial/wizv2/issue.py +775 -27
  11. regscale/integrations/commercial/wizv2/policy_compliance.py +599 -181
  12. regscale/integrations/commercial/wizv2/reports.py +243 -0
  13. regscale/integrations/commercial/wizv2/scanner.py +668 -245
  14. regscale/integrations/compliance_integration.py +304 -51
  15. regscale/integrations/due_date_handler.py +210 -0
  16. regscale/integrations/public/cci_importer.py +444 -0
  17. regscale/integrations/scanner_integration.py +718 -153
  18. regscale/models/integration_models/CCI_List.xml +1 -0
  19. regscale/models/integration_models/cisa_kev_data.json +61 -3
  20. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  21. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +3 -3
  22. regscale/models/regscale_models/form_field_value.py +1 -1
  23. regscale/models/regscale_models/milestone.py +1 -0
  24. regscale/models/regscale_models/regscale_model.py +225 -60
  25. regscale/models/regscale_models/security_plan.py +3 -2
  26. regscale/regscale.py +7 -0
  27. {regscale_cli-6.23.0.0.dist-info → regscale_cli-6.24.0.0.dist-info}/METADATA +9 -9
  28. {regscale_cli-6.23.0.0.dist-info → regscale_cli-6.24.0.0.dist-info}/RECORD +44 -27
  29. tests/fixtures/test_fixture.py +13 -8
  30. tests/regscale/integrations/public/__init__.py +0 -0
  31. tests/regscale/integrations/public/test_alienvault.py +220 -0
  32. tests/regscale/integrations/public/test_cci.py +458 -0
  33. tests/regscale/integrations/public/test_cisa.py +1021 -0
  34. tests/regscale/integrations/public/test_emass.py +518 -0
  35. tests/regscale/integrations/public/test_fedramp.py +851 -0
  36. tests/regscale/integrations/public/test_fedramp_cis_crm.py +3661 -0
  37. tests/regscale/integrations/public/test_file_uploads.py +506 -0
  38. tests/regscale/integrations/public/test_oscal.py +453 -0
  39. tests/regscale/models/test_form_field_value_integration.py +304 -0
  40. tests/regscale/models/test_module_integration.py +582 -0
  41. {regscale_cli-6.23.0.0.dist-info → regscale_cli-6.24.0.0.dist-info}/LICENSE +0 -0
  42. {regscale_cli-6.23.0.0.dist-info → regscale_cli-6.24.0.0.dist-info}/WHEEL +0 -0
  43. {regscale_cli-6.23.0.0.dist-info → regscale_cli-6.24.0.0.dist-info}/entry_points.txt +0 -0
  44. {regscale_cli-6.23.0.0.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