regscale-cli 6.26.0.0__py3-none-any.whl → 6.27.0.1__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 +1 -1
- regscale/core/app/internal/evidence.py +419 -2
- regscale/dev/code_gen.py +24 -20
- regscale/integrations/commercial/__init__.py +0 -1
- regscale/integrations/commercial/jira.py +367 -126
- regscale/integrations/commercial/qualys/__init__.py +7 -8
- regscale/integrations/commercial/qualys/scanner.py +8 -3
- regscale/integrations/commercial/synqly/assets.py +17 -0
- regscale/integrations/commercial/synqly/vulnerabilities.py +45 -28
- regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
- regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
- regscale/integrations/commercial/tenablev2/commands.py +142 -1
- regscale/integrations/commercial/tenablev2/scanner.py +0 -1
- regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
- regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
- regscale/integrations/commercial/wizv2/click.py +44 -59
- regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
- regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
- regscale/integrations/commercial/wizv2/compliance_report.py +10 -9
- regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
- regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +3 -3
- regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +1 -17
- regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
- regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
- regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +5 -9
- regscale/integrations/commercial/wizv2/issue.py +1 -1
- regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
- regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
- regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
- regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
- regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
- regscale/integrations/commercial/wizv2/reports.py +1 -1
- regscale/integrations/commercial/wizv2/sbom.py +1 -1
- regscale/integrations/commercial/wizv2/scanner.py +40 -100
- regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
- regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
- regscale/integrations/commercial/wizv2/variables.py +89 -3
- regscale/integrations/compliance_integration.py +0 -46
- regscale/integrations/control_matcher.py +22 -3
- regscale/integrations/due_date_handler.py +14 -8
- regscale/integrations/public/fedramp/docx_parser.py +10 -1
- regscale/integrations/public/fedramp/fedramp_cis_crm.py +393 -340
- regscale/integrations/public/fedramp/fedramp_five.py +1 -1
- regscale/integrations/scanner_integration.py +127 -57
- regscale/models/integration_models/cisa_kev_data.json +132 -9
- regscale/models/integration_models/qualys.py +3 -4
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +24 -7
- regscale/models/integration_models/synqly_models/synqly_model.py +8 -1
- regscale/models/regscale_models/control_implementation.py +1 -1
- regscale/models/regscale_models/issue.py +0 -1
- {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.1.dist-info}/METADATA +1 -17
- {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.1.dist-info}/RECORD +94 -61
- tests/regscale/integrations/commercial/test_jira.py +481 -91
- tests/regscale/integrations/commercial/test_wiz.py +96 -200
- tests/regscale/integrations/commercial/wizv2/__init__.py +1 -1
- tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
- tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
- tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
- tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
- tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
- tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
- tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
- tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
- tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
- tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
- tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
- tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
- tests/regscale/integrations/commercial/wizv2/test_issue.py +1 -1
- tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
- tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
- tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
- tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +1 -1
- tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +72 -29
- tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
- tests/regscale/integrations/commercial/wizv2/test_wizv2.py +946 -78
- tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +97 -202
- tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
- tests/regscale/integrations/public/test_fedramp.py +301 -0
- tests/regscale/integrations/test_control_matcher.py +83 -0
- regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3543
- tests/regscale/integrations/commercial/wizv2/test_wiz_policy_compliance.py +0 -750
- /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
- {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.1.dist-info}/LICENSE +0 -0
- {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.1.dist-info}/WHEEL +0 -0
- {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.1.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.1.dist-info}/top_level.txt +0 -0
|
@@ -849,3 +849,304 @@ class TestFedRamp(CLITestFixture):
|
|
|
849
849
|
|
|
850
850
|
assert asset.get("ipAddress") is None
|
|
851
851
|
assert asset.get("iPv6Address") is None
|
|
852
|
+
|
|
853
|
+
@staticmethod
|
|
854
|
+
def test_ssp_parser_vertical_table_single_header():
|
|
855
|
+
"""Test vertical table detection with single header (original behavior)."""
|
|
856
|
+
from regscale.integrations.public.fedramp.docx_parser import SSPDocParser
|
|
857
|
+
from unittest.mock import MagicMock
|
|
858
|
+
|
|
859
|
+
xml_content = b"""
|
|
860
|
+
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
861
|
+
<w:tbl>
|
|
862
|
+
<w:tr>
|
|
863
|
+
<w:tc><w:p><w:r><w:t>System Owner Information</w:t></w:r></w:p></w:tc>
|
|
864
|
+
</w:tr>
|
|
865
|
+
<w:tr>
|
|
866
|
+
<w:tc><w:p><w:r><w:t>Name</w:t></w:r></w:p></w:tc>
|
|
867
|
+
<w:tc><w:p><w:r><w:t>John Doe</w:t></w:r></w:p></w:tc>
|
|
868
|
+
</w:tr>
|
|
869
|
+
<w:tr>
|
|
870
|
+
<w:tc><w:p><w:r><w:t>Title</w:t></w:r></w:p></w:tc>
|
|
871
|
+
<w:tc><w:p><w:r><w:t>System Owner</w:t></w:r></w:p></w:tc>
|
|
872
|
+
</w:tr>
|
|
873
|
+
</w:tbl>
|
|
874
|
+
</w:document>
|
|
875
|
+
"""
|
|
876
|
+
|
|
877
|
+
parser = SSPDocParser.__new__(SSPDocParser)
|
|
878
|
+
parser.doc = MagicMock()
|
|
879
|
+
parser.doc.element.body = [] # No preceding text needed for this test
|
|
880
|
+
|
|
881
|
+
tables = parser.parse_xml_for_tables(xml_content)
|
|
882
|
+
|
|
883
|
+
assert len(tables) == 1
|
|
884
|
+
table_data = tables[0]["table_data"]
|
|
885
|
+
assert len(table_data) == 2
|
|
886
|
+
assert table_data[0] == {"Name": "John Doe"}
|
|
887
|
+
assert table_data[1] == {"Title": "System Owner"}
|
|
888
|
+
|
|
889
|
+
@staticmethod
|
|
890
|
+
def test_ssp_parser_vertical_table_two_headers_empty_second():
|
|
891
|
+
"""Test vertical table detection with two headers where second is empty (new fix)."""
|
|
892
|
+
from regscale.integrations.public.fedramp.docx_parser import SSPDocParser
|
|
893
|
+
from unittest.mock import MagicMock
|
|
894
|
+
|
|
895
|
+
xml_content = b"""
|
|
896
|
+
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
897
|
+
<w:tbl>
|
|
898
|
+
<w:tr>
|
|
899
|
+
<w:tc><w:p><w:r><w:t>System Information</w:t></w:r></w:p></w:tc>
|
|
900
|
+
<w:tc><w:p><w:r><w:t></w:t></w:r></w:p></w:tc>
|
|
901
|
+
</w:tr>
|
|
902
|
+
<w:tr>
|
|
903
|
+
<w:tc><w:p><w:r><w:t>CSP Name:</w:t></w:r></w:p></w:tc>
|
|
904
|
+
<w:tc><w:p><w:r><w:t>Public Safety Platform (PSP)</w:t></w:r></w:p></w:tc>
|
|
905
|
+
</w:tr>
|
|
906
|
+
<w:tr>
|
|
907
|
+
<w:tc><w:p><w:r><w:t>CSO Name:</w:t></w:r></w:p></w:tc>
|
|
908
|
+
<w:tc><w:p><w:r><w:t>Mark43, Inc.</w:t></w:r></w:p></w:tc>
|
|
909
|
+
</w:tr>
|
|
910
|
+
<w:tr>
|
|
911
|
+
<w:tc><w:p><w:r><w:t>Service Model:</w:t></w:r></w:p></w:tc>
|
|
912
|
+
<w:tc><w:p><w:r><w:t>Software-as-a-Service (SaaS)</w:t></w:r></w:p></w:tc>
|
|
913
|
+
</w:tr>
|
|
914
|
+
</w:tbl>
|
|
915
|
+
</w:document>
|
|
916
|
+
"""
|
|
917
|
+
|
|
918
|
+
parser = SSPDocParser.__new__(SSPDocParser)
|
|
919
|
+
parser.doc = MagicMock()
|
|
920
|
+
parser.doc.element.body = [] # No preceding text needed for this test
|
|
921
|
+
|
|
922
|
+
tables = parser.parse_xml_for_tables(xml_content)
|
|
923
|
+
|
|
924
|
+
assert len(tables) == 1
|
|
925
|
+
table_data = tables[0]["table_data"]
|
|
926
|
+
assert len(table_data) == 3
|
|
927
|
+
assert table_data[0] == {"CSP Name:": "Public Safety Platform (PSP)"}
|
|
928
|
+
assert table_data[1] == {"CSO Name:": "Mark43, Inc."}
|
|
929
|
+
assert table_data[2] == {"Service Model:": "Software-as-a-Service (SaaS)"}
|
|
930
|
+
|
|
931
|
+
@staticmethod
|
|
932
|
+
def test_ssp_parser_vertical_table_two_headers_whitespace_second():
|
|
933
|
+
"""Test vertical table detection with two headers where second is only whitespace."""
|
|
934
|
+
from regscale.integrations.public.fedramp.docx_parser import SSPDocParser
|
|
935
|
+
from unittest.mock import MagicMock
|
|
936
|
+
|
|
937
|
+
xml_content = b"""
|
|
938
|
+
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
939
|
+
<w:tbl>
|
|
940
|
+
<w:tr>
|
|
941
|
+
<w:tc><w:p><w:r><w:t>System Information</w:t></w:r></w:p></w:tc>
|
|
942
|
+
<w:tc><w:p><w:r><w:t> </w:t></w:r></w:p></w:tc>
|
|
943
|
+
</w:tr>
|
|
944
|
+
<w:tr>
|
|
945
|
+
<w:tc><w:p><w:r><w:t>FedRAMP Package ID:</w:t></w:r></w:p></w:tc>
|
|
946
|
+
<w:tc><w:p><w:r><w:t>FR2235965777</w:t></w:r></w:p></w:tc>
|
|
947
|
+
</w:tr>
|
|
948
|
+
<w:tr>
|
|
949
|
+
<w:tc><w:p><w:r><w:t>FIPS PUB 199 Level:</w:t></w:r></w:p></w:tc>
|
|
950
|
+
<w:tc><w:p><w:r><w:t>High</w:t></w:r></w:p></w:tc>
|
|
951
|
+
</w:tr>
|
|
952
|
+
</w:tbl>
|
|
953
|
+
</w:document>
|
|
954
|
+
"""
|
|
955
|
+
|
|
956
|
+
parser = SSPDocParser.__new__(SSPDocParser)
|
|
957
|
+
parser.doc = MagicMock()
|
|
958
|
+
parser.doc.element.body = [] # No preceding text needed for this test
|
|
959
|
+
|
|
960
|
+
tables = parser.parse_xml_for_tables(xml_content)
|
|
961
|
+
|
|
962
|
+
assert len(tables) == 1
|
|
963
|
+
table_data = tables[0]["table_data"]
|
|
964
|
+
assert len(table_data) == 2
|
|
965
|
+
assert table_data[0] == {"FedRAMP Package ID:": "FR2235965777"}
|
|
966
|
+
assert table_data[1] == {"FIPS PUB 199 Level:": "High"}
|
|
967
|
+
|
|
968
|
+
@staticmethod
|
|
969
|
+
def test_ssp_parser_horizontal_table_not_detected_as_vertical():
|
|
970
|
+
"""Test that horizontal tables with meaningful headers are not detected as vertical."""
|
|
971
|
+
from regscale.integrations.public.fedramp.docx_parser import SSPDocParser
|
|
972
|
+
from unittest.mock import MagicMock
|
|
973
|
+
|
|
974
|
+
xml_content = b"""
|
|
975
|
+
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
976
|
+
<w:tbl>
|
|
977
|
+
<w:tr>
|
|
978
|
+
<w:tc><w:p><w:r><w:t>Date</w:t></w:r></w:p></w:tc>
|
|
979
|
+
<w:tc><w:p><w:r><w:t>Description</w:t></w:r></w:p></w:tc>
|
|
980
|
+
<w:tc><w:p><w:r><w:t>Version</w:t></w:r></w:p></w:tc>
|
|
981
|
+
</w:tr>
|
|
982
|
+
<w:tr>
|
|
983
|
+
<w:tc><w:p><w:r><w:t>05/14/2024</w:t></w:r></w:p></w:tc>
|
|
984
|
+
<w:tc><w:p><w:r><w:t>Initial version</w:t></w:r></w:p></w:tc>
|
|
985
|
+
<w:tc><w:p><w:r><w:t>1.0</w:t></w:r></w:p></w:tc>
|
|
986
|
+
</w:tr>
|
|
987
|
+
<w:tr>
|
|
988
|
+
<w:tc><w:p><w:r><w:t>11/01/2024</w:t></w:r></w:p></w:tc>
|
|
989
|
+
<w:tc><w:p><w:r><w:t>Updated version</w:t></w:r></w:p></w:tc>
|
|
990
|
+
<w:tc><w:p><w:r><w:t>2.0</w:t></w:r></w:p></w:tc>
|
|
991
|
+
</w:tr>
|
|
992
|
+
</w:tbl>
|
|
993
|
+
</w:document>
|
|
994
|
+
"""
|
|
995
|
+
|
|
996
|
+
parser = SSPDocParser.__new__(SSPDocParser)
|
|
997
|
+
parser.doc = MagicMock()
|
|
998
|
+
parser.doc.element.body = [] # No preceding text needed for this test
|
|
999
|
+
|
|
1000
|
+
tables = parser.parse_xml_for_tables(xml_content)
|
|
1001
|
+
|
|
1002
|
+
assert len(tables) == 1
|
|
1003
|
+
table_data = tables[0]["table_data"]
|
|
1004
|
+
assert len(table_data) == 2
|
|
1005
|
+
# Should be horizontal format with named columns
|
|
1006
|
+
assert table_data[0] == {"Date": "05/14/2024", "Description": "Initial version", "Version": "1.0"}
|
|
1007
|
+
assert table_data[1] == {"Date": "11/01/2024", "Description": "Updated version", "Version": "2.0"}
|
|
1008
|
+
|
|
1009
|
+
@staticmethod
|
|
1010
|
+
def test_ssp_parser_two_column_table_non_vertical():
|
|
1011
|
+
"""Test that 2-column tables with meaningful second header are not detected as vertical."""
|
|
1012
|
+
from regscale.integrations.public.fedramp.docx_parser import SSPDocParser
|
|
1013
|
+
from unittest.mock import MagicMock
|
|
1014
|
+
|
|
1015
|
+
xml_content = b"""
|
|
1016
|
+
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
1017
|
+
<w:tbl>
|
|
1018
|
+
<w:tr>
|
|
1019
|
+
<w:tc><w:p><w:r><w:t>Security Objective</w:t></w:r></w:p></w:tc>
|
|
1020
|
+
<w:tc><w:p><w:r><w:t>Level</w:t></w:r></w:p></w:tc>
|
|
1021
|
+
</w:tr>
|
|
1022
|
+
<w:tr>
|
|
1023
|
+
<w:tc><w:p><w:r><w:t>Confidentiality</w:t></w:r></w:p></w:tc>
|
|
1024
|
+
<w:tc><w:p><w:r><w:t>High</w:t></w:r></w:p></w:tc>
|
|
1025
|
+
</w:tr>
|
|
1026
|
+
<w:tr>
|
|
1027
|
+
<w:tc><w:p><w:r><w:t>Integrity</w:t></w:r></w:p></w:tc>
|
|
1028
|
+
<w:tc><w:p><w:r><w:t>High</w:t></w:r></w:p></w:tc>
|
|
1029
|
+
</w:tr>
|
|
1030
|
+
</w:tbl>
|
|
1031
|
+
</w:document>
|
|
1032
|
+
"""
|
|
1033
|
+
|
|
1034
|
+
parser = SSPDocParser.__new__(SSPDocParser)
|
|
1035
|
+
parser.doc = MagicMock()
|
|
1036
|
+
parser.doc.element.body = [] # No preceding text needed for this test
|
|
1037
|
+
|
|
1038
|
+
tables = parser.parse_xml_for_tables(xml_content)
|
|
1039
|
+
|
|
1040
|
+
assert len(tables) == 1
|
|
1041
|
+
table_data = tables[0]["table_data"]
|
|
1042
|
+
assert len(table_data) == 2
|
|
1043
|
+
# Should be horizontal format since second header is not empty
|
|
1044
|
+
assert table_data[0] == {"Security Objective": "Confidentiality", "Level": "High"}
|
|
1045
|
+
assert table_data[1] == {"Security Objective": "Integrity", "Level": "High"}
|
|
1046
|
+
|
|
1047
|
+
@staticmethod
|
|
1048
|
+
def test_ssp_parser_vertical_table_first_header_not_in_list():
|
|
1049
|
+
"""Test that tables with first header not in vertical_tables list are not detected as vertical."""
|
|
1050
|
+
from regscale.integrations.public.fedramp.docx_parser import SSPDocParser
|
|
1051
|
+
from unittest.mock import MagicMock
|
|
1052
|
+
|
|
1053
|
+
xml_content = b"""
|
|
1054
|
+
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
1055
|
+
<w:tbl>
|
|
1056
|
+
<w:tr>
|
|
1057
|
+
<w:tc><w:p><w:r><w:t>Random Table Header</w:t></w:r></w:p></w:tc>
|
|
1058
|
+
<w:tc><w:p><w:r><w:t></w:t></w:r></w:p></w:tc>
|
|
1059
|
+
</w:tr>
|
|
1060
|
+
<w:tr>
|
|
1061
|
+
<w:tc><w:p><w:r><w:t>Field One:</w:t></w:r></w:p></w:tc>
|
|
1062
|
+
<w:tc><w:p><w:r><w:t>Value One</w:t></w:r></w:p></w:tc>
|
|
1063
|
+
</w:tr>
|
|
1064
|
+
<w:tr>
|
|
1065
|
+
<w:tc><w:p><w:r><w:t>Field Two:</w:t></w:r></w:p></w:tc>
|
|
1066
|
+
<w:tc><w:p><w:r><w:t>Value Two</w:t></w:r></w:p></w:tc>
|
|
1067
|
+
</w:tr>
|
|
1068
|
+
</w:tbl>
|
|
1069
|
+
</w:document>
|
|
1070
|
+
"""
|
|
1071
|
+
|
|
1072
|
+
parser = SSPDocParser.__new__(SSPDocParser)
|
|
1073
|
+
parser.doc = MagicMock()
|
|
1074
|
+
parser.doc.element.body = [] # No preceding text needed for this test
|
|
1075
|
+
|
|
1076
|
+
tables = parser.parse_xml_for_tables(xml_content)
|
|
1077
|
+
|
|
1078
|
+
assert len(tables) == 1
|
|
1079
|
+
table_data = tables[0]["table_data"]
|
|
1080
|
+
assert len(table_data) == 2
|
|
1081
|
+
# Should be horizontal format since "Random Table Header" is not in vertical_tables list
|
|
1082
|
+
assert table_data[0] == {"Random Table Header": "Field One:", "": "Value One"}
|
|
1083
|
+
assert table_data[1] == {"Random Table Header": "Field Two:", "": "Value Two"}
|
|
1084
|
+
|
|
1085
|
+
@staticmethod
|
|
1086
|
+
def test_ssp_parser_all_vertical_table_types():
|
|
1087
|
+
"""Test all table types that should be detected as vertical."""
|
|
1088
|
+
from regscale.integrations.public.fedramp.docx_parser import SSPDocParser
|
|
1089
|
+
from unittest.mock import MagicMock
|
|
1090
|
+
|
|
1091
|
+
vertical_table_headers = [
|
|
1092
|
+
"Identification of Organization that Prepared this Document",
|
|
1093
|
+
"Identification of Cloud Service Provider",
|
|
1094
|
+
"System Owner Information",
|
|
1095
|
+
"System Information",
|
|
1096
|
+
"System Component Information",
|
|
1097
|
+
"ISSO (or Equivalent) Point of Contact",
|
|
1098
|
+
]
|
|
1099
|
+
|
|
1100
|
+
for header in vertical_table_headers:
|
|
1101
|
+
# Test with single header
|
|
1102
|
+
xml_content = f"""
|
|
1103
|
+
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
1104
|
+
<w:tbl>
|
|
1105
|
+
<w:tr>
|
|
1106
|
+
<w:tc><w:p><w:r><w:t>{header}</w:t></w:r></w:p></w:tc>
|
|
1107
|
+
</w:tr>
|
|
1108
|
+
<w:tr>
|
|
1109
|
+
<w:tc><w:p><w:r><w:t>Field:</w:t></w:r></w:p></w:tc>
|
|
1110
|
+
<w:tc><w:p><w:r><w:t>Value</w:t></w:r></w:p></w:tc>
|
|
1111
|
+
</w:tr>
|
|
1112
|
+
</w:tbl>
|
|
1113
|
+
</w:document>
|
|
1114
|
+
""".encode()
|
|
1115
|
+
|
|
1116
|
+
parser = SSPDocParser.__new__(SSPDocParser)
|
|
1117
|
+
parser.doc = MagicMock()
|
|
1118
|
+
parser.doc.element.body = [] # No preceding text needed for this test
|
|
1119
|
+
|
|
1120
|
+
tables = parser.parse_xml_for_tables(xml_content)
|
|
1121
|
+
|
|
1122
|
+
assert len(tables) == 1
|
|
1123
|
+
table_data = tables[0]["table_data"]
|
|
1124
|
+
assert len(table_data) == 1
|
|
1125
|
+
assert table_data[0] == {"Field:": "Value"}
|
|
1126
|
+
|
|
1127
|
+
# Test with two headers (empty second)
|
|
1128
|
+
xml_content_two_headers = f"""
|
|
1129
|
+
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
1130
|
+
<w:tbl>
|
|
1131
|
+
<w:tr>
|
|
1132
|
+
<w:tc><w:p><w:r><w:t>{header}</w:t></w:r></w:p></w:tc>
|
|
1133
|
+
<w:tc><w:p><w:r><w:t></w:t></w:r></w:p></w:tc>
|
|
1134
|
+
</w:tr>
|
|
1135
|
+
<w:tr>
|
|
1136
|
+
<w:tc><w:p><w:r><w:t>Field:</w:t></w:r></w:p></w:tc>
|
|
1137
|
+
<w:tc><w:p><w:r><w:t>Value</w:t></w:r></w:p></w:tc>
|
|
1138
|
+
</w:tr>
|
|
1139
|
+
</w:tbl>
|
|
1140
|
+
</w:document>
|
|
1141
|
+
""".encode()
|
|
1142
|
+
|
|
1143
|
+
parser2 = SSPDocParser.__new__(SSPDocParser)
|
|
1144
|
+
parser2.doc = MagicMock()
|
|
1145
|
+
parser2.doc.element.body = [] # No preceding text needed for this test
|
|
1146
|
+
|
|
1147
|
+
tables2 = parser2.parse_xml_for_tables(xml_content_two_headers)
|
|
1148
|
+
|
|
1149
|
+
assert len(tables2) == 1
|
|
1150
|
+
table_data2 = tables2[0]["table_data"]
|
|
1151
|
+
assert len(table_data2) == 1
|
|
1152
|
+
assert table_data2[0] == {"Field:": "Value"}
|
|
@@ -201,6 +201,89 @@ class TestControlMatcherParseControlId:
|
|
|
201
201
|
assert result is None, f"Expected None for input {input_id}, got {result}"
|
|
202
202
|
|
|
203
203
|
|
|
204
|
+
class TestControlMatcherParseControlIdWithSpaces:
|
|
205
|
+
"""Test cases for parse_control_id method with spaces in control IDs"""
|
|
206
|
+
|
|
207
|
+
@patch("regscale.integrations.control_matcher.Api")
|
|
208
|
+
@patch("regscale.integrations.control_matcher.Application")
|
|
209
|
+
def test_parse_control_id_with_space_before_parenthesis(self, mock_app_class, mock_api_class):
|
|
210
|
+
"""Test parsing control ID with space before parenthesis"""
|
|
211
|
+
matcher = ControlMatcher()
|
|
212
|
+
test_cases = [
|
|
213
|
+
("AC-1 (1)", "AC-1.1"),
|
|
214
|
+
("AC-2 (3)", "AC-2.3"),
|
|
215
|
+
("SI-4 (10)", "SI-4.10"),
|
|
216
|
+
("CM-6 (1)", "CM-6.1"),
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
for input_id, expected in test_cases:
|
|
220
|
+
result = matcher.parse_control_id(input_id)
|
|
221
|
+
assert result == expected, f"Failed for input {input_id}"
|
|
222
|
+
|
|
223
|
+
@patch("regscale.integrations.control_matcher.Api")
|
|
224
|
+
@patch("regscale.integrations.control_matcher.Application")
|
|
225
|
+
def test_parse_control_id_with_spaces_inside_parentheses(self, mock_app_class, mock_api_class):
|
|
226
|
+
"""Test parsing control ID with spaces inside parentheses"""
|
|
227
|
+
matcher = ControlMatcher()
|
|
228
|
+
test_cases = [
|
|
229
|
+
("AC-1( 1 )", "AC-1.1"),
|
|
230
|
+
("AC-2( 3 )", "AC-2.3"),
|
|
231
|
+
("SI-4( 10)", "SI-4.10"),
|
|
232
|
+
("CM-6(1 )", "CM-6.1"),
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
for input_id, expected in test_cases:
|
|
236
|
+
result = matcher.parse_control_id(input_id)
|
|
237
|
+
assert result == expected, f"Failed for input {input_id}"
|
|
238
|
+
|
|
239
|
+
@patch("regscale.integrations.control_matcher.Api")
|
|
240
|
+
@patch("regscale.integrations.control_matcher.Application")
|
|
241
|
+
def test_parse_control_id_with_leading_zeros_and_spaces(self, mock_app_class, mock_api_class):
|
|
242
|
+
"""Test parsing control ID with both leading zeros and spaces"""
|
|
243
|
+
matcher = ControlMatcher()
|
|
244
|
+
test_cases = [
|
|
245
|
+
("AC-01 (01)", "AC-1.1"),
|
|
246
|
+
("AC-02 (04)", "AC-2.4"),
|
|
247
|
+
("AC-17 (02)", "AC-17.2"),
|
|
248
|
+
("SI-04 (05)", "SI-4.5"),
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
for input_id, expected in test_cases:
|
|
252
|
+
result = matcher.parse_control_id(input_id)
|
|
253
|
+
assert result == expected, f"Failed for input {input_id}"
|
|
254
|
+
|
|
255
|
+
@patch("regscale.integrations.control_matcher.Api")
|
|
256
|
+
@patch("regscale.integrations.control_matcher.Application")
|
|
257
|
+
def test_parse_control_id_with_various_space_combinations(self, mock_app_class, mock_api_class):
|
|
258
|
+
"""Test parsing control ID with various space combinations"""
|
|
259
|
+
matcher = ControlMatcher()
|
|
260
|
+
test_cases = [
|
|
261
|
+
("AC-1 (1)", "AC-1.1"), # Multiple spaces before
|
|
262
|
+
("AC-1 ( 1 )", "AC-1.1"), # Spaces everywhere
|
|
263
|
+
("AC-1 ( 1 )", "AC-1.1"), # Multiple spaces everywhere
|
|
264
|
+
("AC-01 ( 04 )", "AC-1.4"), # Leading zeros and multiple spaces
|
|
265
|
+
]
|
|
266
|
+
|
|
267
|
+
for input_id, expected in test_cases:
|
|
268
|
+
result = matcher.parse_control_id(input_id)
|
|
269
|
+
assert result == expected, f"Failed for input {input_id}"
|
|
270
|
+
|
|
271
|
+
@patch("regscale.integrations.control_matcher.Api")
|
|
272
|
+
@patch("regscale.integrations.control_matcher.Application")
|
|
273
|
+
def test_parse_control_id_with_spaces_in_text(self, mock_app_class, mock_api_class):
|
|
274
|
+
"""Test parsing control ID with spaces in descriptive text"""
|
|
275
|
+
matcher = ControlMatcher()
|
|
276
|
+
test_cases = [
|
|
277
|
+
("Access Control AC-1 (1)", "AC-1.1"),
|
|
278
|
+
("AC-2 (3) Account Management", "AC-2.3"),
|
|
279
|
+
("NIST Control AC-17 (02)", "AC-17.2"),
|
|
280
|
+
]
|
|
281
|
+
|
|
282
|
+
for input_id, expected in test_cases:
|
|
283
|
+
result = matcher.parse_control_id(input_id)
|
|
284
|
+
assert result == expected, f"Failed for input {input_id}"
|
|
285
|
+
|
|
286
|
+
|
|
204
287
|
class TestControlMatcherFindControlInCatalog:
|
|
205
288
|
"""Test cases for find_control_in_catalog method"""
|
|
206
289
|
|