-
Notifications
You must be signed in to change notification settings - Fork 5
Feature/additional secrets improvements #80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 5 commits
5843f1d
945bf92
9d1e0cb
1ea6dd4
354e407
e796e09
f25153c
d96978f
ff53162
a2e6cef
6a3be62
a61fe8e
58e81c1
f01f477
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -149,19 +149,33 @@ def __redact_request_dictionary( | |
def __redact_string( | ||
self, | ||
input_string: Union[str, bytes], | ||
start_ind: int, | ||
end_pattern: Union[str, bytes], | ||
): | ||
key: str, | ||
) -> Union[str, bytes]: | ||
""" | ||
Redacts characters in a string between a starting index and ending pattern. | ||
Redacts characters in a string between a starting index and ending tag. | ||
Replaces the identified characters with '********' regardless of the original length. | ||
""" | ||
asterisks = "********" | ||
is_bytes = False | ||
if isinstance(input_string, bytes): | ||
asterisks = asterisks.encode("cp1047") | ||
pre_keyword = input_string[:start_ind] | ||
post_keyword = end_pattern.join(input_string[start_ind:].split(end_pattern)[1:]) | ||
return pre_keyword + asterisks + end_pattern + post_keyword | ||
input_string = input_string.decode("cp1047") | ||
is_bytes = True | ||
quoted = re.search(rf"{key.upper()}( +)\(\'.*?(?<!\')\'\)", input_string) | ||
if quoted is not None: | ||
input_string = re.sub( | ||
rf"{key.upper()}( +)\(\'.*?(?<!\')\'\)", | ||
rf"{key.upper()}\1('{asterisks}')", | ||
input_string, | ||
) | ||
else: | ||
input_string = re.sub( | ||
rf"{key.upper()}( +)\(.*?(?<!\\)\)", | ||
rf"{key.upper()}\1({asterisks})", | ||
input_string, | ||
) | ||
lcarcaramo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if is_bytes: | ||
return input_string.encode("cp1047") | ||
return input_string | ||
|
||
def redact_request_xml( | ||
self, | ||
|
@@ -191,7 +205,11 @@ def redact_request_xml( | |
# <tag operation="del" /> | ||
if f"</{xml_key}>" not in xml_string: | ||
continue | ||
xml_string = self.__redact_string(xml_string, match.end(), f"</{xml_key}") | ||
xml_string = re.sub( | ||
lcarcaramo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
rf"{xml_key}(.*)>.*<\/{xml_key}", | ||
rf"{xml_key}\1>********</{xml_key}", | ||
xml_string, | ||
) | ||
lcarcaramo marked this conversation as resolved.
Show resolved
Hide resolved
lcarcaramo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if is_bytes: | ||
xml_string = xml_string.encode("utf-8") | ||
return xml_string | ||
|
@@ -203,27 +221,38 @@ def redact_result_xml( | |
) -> str: | ||
""" | ||
Redacts a list of specific secret traits in a result xml string. | ||
Based on the following RACF command pattern: | ||
Based on the following RACF command patterns: | ||
'TRAIT (value)' | ||
"TRAIT ('value')" | ||
This function also accounts for varied amounts of whitespace in the pattern. | ||
""" | ||
if isinstance(security_result, list): | ||
return security_result | ||
for xml_key in secret_traits.values(): | ||
racf_key = xml_key.split(":")[1] if ":" in xml_key else xml_key | ||
end_pattern = ")" | ||
if isinstance(security_result, bytes): | ||
match = re.search( | ||
rf"{racf_key.upper()} +\(", security_result.decode("cp1047") | ||
) | ||
end_pattern = end_pattern.encode("cp1047") | ||
else: | ||
match = re.search(rf"{racf_key.upper()} +\(", security_result) | ||
if not match: | ||
continue | ||
security_result = self.__redact_string( | ||
security_result, match.end(), end_pattern | ||
) | ||
security_result = self.__redact_string(security_result, racf_key) | ||
if isinstance(security_result, bytes): | ||
security_result = re.sub( | ||
rf"<message>([A-Z]*[0-9]*[A-Z]) [^<>]*{racf_key.upper()}[^<>]*<\/message>", | ||
rf"<message>REDACTED MESSAGE CONCERNING {racf_key.upper()}, " | ||
+ r"REVIEW DOCUMENTATION OF \1 FOR MORE INFORMATION</message>", | ||
security_result.decode("cp1047"), | ||
).encode("cp1047") | ||
else: | ||
security_result = re.sub( | ||
rf"<message>([A-Z]*[0-9]*[A-Z]) [^<>]*{racf_key.upper()}[^<>]*<\/message>", | ||
rf"<message>REDACTED MESSAGE CONCERNING {racf_key.upper()}, " | ||
+ r"REVIEW DOCUMENTATION OF \1 FOR MORE INFORMATION</message>", | ||
lcarcaramo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
security_result, | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also explain how these regexes work. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added the following:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a slight working change suggestion:
Instead of
|
||
return security_result | ||
|
||
def __colorize_json(self, json_text: str) -> str: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -523,3 +523,27 @@ def test_user_admin_custom_secret_redacted_on_error( | |
f"({TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR['omvs:uid']})", | ||
exception.exception.result, | ||
) | ||
|
||
def test_user_admin_custom_secret_redacted_when_complex_characters( | ||
self, | ||
call_racf_mock: Mock, | ||
): | ||
user_admin = UserAdmin(additional_secret_traits=["base:installation_data"]) | ||
call_racf_mock.side_effect = [ | ||
TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, | ||
TestUserConstants.TEST_ALTER_USER_RESULT_INST_DATA_SUCCESS_XML, | ||
] | ||
result = user_admin.alter( | ||
"squidwrd", | ||
traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA, | ||
) | ||
self.assertEqual( | ||
result, | ||
TestUserConstants.TEST_ALTER_USER_RESULT_SUCCESS_INST_DATA_SECRET_DICTIONARY, | ||
) | ||
self.assertNotIn( | ||
TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA[ | ||
"base:installation_data" | ||
], | ||
result, | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if there should be an extract test too for the complex installation data? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually am working on stabilizing this more fully in my RAS branch. There is a new test but it required new extract logic to actually work. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would still not consider INSTALLATION_DATA "Stable" at the end of this branch, but once this is approved, I would consider additional_secrets_redaction "stable". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to confirm, redaction for profile extract is not within the scope of this pull request? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?xml version="1.0" encoding="IBM-1047"?> | ||
<securityresult xmlns="http://www.ibm.com/systems/zos/saf/IRRSMO00Result1"> | ||
<user name="SQUIDWRD" operation="set" requestid="UserRequest"> | ||
<info>Definition exists. Add command skipped due to precheck option</info> | ||
<command> | ||
<safreturncode>0</safreturncode> | ||
<returncode>0</returncode> | ||
<reasoncode>0</reasoncode> | ||
<image>ALTUSER SQUIDWRD NAME ('Squidward') OWNER (leonard) SPECIAL DATA ('Test = Value; Other(stuff goes here)'')') OMVS (UID (2424) HOME ('/u/squidwrd') PROGRAM ('/bin/sh'))</image> | ||
</command> | ||
</user> | ||
<returncode>0</returncode> | ||
<reasoncode>0</reasoncode> | ||
</securityresult> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"securityResult": { | ||
"user": { | ||
"name": "SQUIDWRD", | ||
"operation": "set", | ||
"requestId": "UserRequest", | ||
"info": [ | ||
"Definition exists. Add command skipped due to precheck option" | ||
], | ||
"commands": [ | ||
{ | ||
"safReturnCode": 0, | ||
"returnCode": 0, | ||
"reasonCode": 0, | ||
"image": "ALTUSER SQUIDWRD NAME ('Squidward') OWNER (leonard) SPECIAL DATA ('********') OMVS (UID (2424) HOME ('/u/squidwrd') PROGRAM ('/bin/sh'))" | ||
} | ||
] | ||
}, | ||
"returnCode": 0, | ||
"reasonCode": 0, | ||
"runningUserid": "testuser" | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.