-
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 9 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,30 +149,97 @@ def __redact_request_dictionary( | |
def __redact_string( | ||
self, | ||
input_string: Union[str, bytes], | ||
start_ind: int, | ||
end_pattern: Union[str, bytes], | ||
): | ||
""" | ||
Redacts characters in a string between a starting index and ending pattern. | ||
trait_key: str, | ||
) -> Union[str, bytes]: | ||
r""" | ||
Redacts characters in a string between a starting index and ending tag. | ||
Replaces the identified characters with '********' regardless of the original length. | ||
|
||
This function employs the following regular expressions explained below | ||
|
||
Regex 1 ("quoted") - {trait_key.upper()}( +){{0,}}\(\'.*?(?<!\')\'\) | ||
This is designed to match the pattern TRAIT('value') by matching the TRAIT name, a | ||
variable (potentially zero) amount of spaces, then the ('value') portion which must | ||
start and end with (' and '), but can conceivably contain any characters, but a negative | ||
lookbehind is used to look for any unescaped single quotes that would indicate the matching | ||
of ') is otherwise a coincidence. | ||
|
||
Regex 2 ("nested") - {trait_key.upper()}( +){{0,}}\([^)]*\(.*\)( +){{0,}}\) | ||
This is designed to match the pattern TRAIT( subtrait1(value) subtrait2(value)) by | ||
matching the TRAIT name, a variable (potentially zero) amount of spaces, then the | ||
( subtrait1(value) subtrait2(value)) portion which must start and end with ( and ), | ||
but must also contain a nested set of opened parentheses rather than a direct seqence of | ||
lcarcaramo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
them. The pattern looks for these nested open parenthesis as a sequence would have a ) | ||
character between them. Then the expression allows any non-newline characters and the | ||
"end pattern" of ) and ) separated by a variable (potentially zero) whitespace. | ||
|
||
If neither of these two patterns is found for a supplied trait_key, then it is assumed | ||
this trait is set with the default pattern below. | ||
|
||
Regex 3 ("default") - {trait_key.upper()}( +){{0,}}\(.*?(?<!\\)\) | ||
This is designed to match the pattern TRAIT(value) by matching the TRAIT name, a variable | ||
(potentially zero) amount of spaces, then the (value) portion which must start and end | ||
with ( and ), but can conceivably contain any characters, but a negative lookbehind | ||
is used to look for any escape \ character that would indicate the matching of the | ||
lcarcaramo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
( and ) is otherwise a coincidence. | ||
|
||
In all replacement expressions, the variable amounts of whitespace are captured so that | ||
they can be preserved by this redaction operations. | ||
|
||
""" | ||
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"{trait_key.upper()}( +){{0,}}\(\'.*?(?<!\')\'\)", input_string | ||
lcarcaramo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
nested = re.search( | ||
rf"{trait_key.upper()}( +){{0,}}\([^)]*\(.*\)( +){{0,}}\)", input_string | ||
) | ||
if quoted is not None: | ||
input_string = re.sub( | ||
rf"{trait_key.upper()}( +){{0,}}\(\'.*?(?<!\')\'\)", | ||
rf"{trait_key.upper()}\1('{asterisks}')", | ||
input_string, | ||
) | ||
else: | ||
if nested is not None: | ||
input_string = re.sub( | ||
rf"{trait_key.upper()}( +){{0,}}\([^)]*\(.*\)( +){{0,}}\)", | ||
rf"{trait_key.upper()}\1({asterisks})", | ||
input_string, | ||
) | ||
else: | ||
input_string = re.sub( | ||
rf"{trait_key.upper()}( +){{0,}}\(.*?(?<!\\)\)", | ||
rf"{trait_key.upper()}\1({asterisks})", | ||
input_string, | ||
) | ||
if is_bytes: | ||
return input_string.encode("cp1047") | ||
return input_string | ||
|
||
def redact_request_xml( | ||
self, | ||
xml_string: Union[str, bytes], | ||
secret_traits: dict, | ||
) -> Union[str, bytes]: | ||
""" | ||
r""" | ||
lcarcaramo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Redact a list of specific secret traits in a request xml string or bytes object. | ||
Based the following xml pattern: | ||
'<xmltag attribute="any">xml value</xmltag>' | ||
This function also accounts for any number of arbitrary xml attributes. | ||
|
||
This function employs the following regular expression: | ||
{xml_key}(.*)>.*<\/{xml_key} - Designed to match the above pattern by starting and ending | ||
with the xmltag string as shown, but the starting tage allows for any characters between | ||
lcarcaramo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"xmltag" and the > character to allow for the attribute specification shown above. This | ||
results in the starting of the xml as {xml_key}(.*)> and the ending as <\/{xml_key}. | ||
The miscellaneous characters are "captured" as a variable and preserved by the | ||
lcarcaramo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
substitution operation through the use of the \1 supplied in the replacement string. | ||
Between these tags, any non-newline characters are allowed using the .* expression. | ||
""" | ||
is_bytes = False | ||
if isinstance(xml_string, bytes): | ||
|
@@ -191,7 +258,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 | ||
|
@@ -200,30 +271,49 @@ def redact_result_xml( | |
self, | ||
security_result: Union[str, bytes, List[int]], | ||
secret_traits: dict, | ||
racf_key_map: dict = {}, | ||
racf_message_key_map: dict = {}, | ||
) -> 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 (subtrait1(value) subtrait2(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 racf_key in racf_key_map: | ||
print(racf_key_map[racf_key]) | ||
lcarcaramo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
racf_key = racf_key_map[racf_key] | ||
if isinstance(security_result, bytes): | ||
match = re.search( | ||
rf"{racf_key.upper()} +\(", security_result.decode("cp1047") | ||
rf"{racf_key.upper()}( +){{0,}}\(", security_result.decode("cp1047") | ||
) | ||
end_pattern = end_pattern.encode("cp1047") | ||
else: | ||
match = re.search(rf"{racf_key.upper()} +\(", security_result) | ||
match = re.search(rf"{racf_key.upper()}( +){{0,}}\(", 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. match = re.search(racf_command_argument_regex, 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 racf_key in racf_message_key_map: | ||
racf_key = racf_message_key_map[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: | ||
|
@@ -355,7 +445,13 @@ def __indent_xml(self, minified_xml: str) -> str: | |
indented_xml += f"{' ' * indent_level}{current_line}\n" | ||
return indented_xml[:-2] | ||
|
||
def log_hex_dump(self, raw_result_xml: bytes, secret_traits: dict) -> None: | ||
def log_hex_dump( | ||
self, | ||
raw_result_xml: bytes, | ||
secret_traits: dict, | ||
racf_key_map: dict, | ||
racf_message_key_map: dict, | ||
) -> None: | ||
""" | ||
Log the raw result XML returned by IRRSMO00 as a hex dump. | ||
""" | ||
|
@@ -368,6 +464,8 @@ def log_hex_dump(self, raw_result_xml: bytes, secret_traits: dict) -> None: | |
raw_result_xml = self.redact_result_xml( | ||
raw_result_xml, | ||
secret_traits, | ||
racf_key_map, | ||
racf_message_key_map, | ||
) | ||
for byte in raw_result_xml: | ||
color_function = self.__green | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"securityResult": { | ||
"dataSet": { | ||
"name": "ESWIFT.TEST.T113622.P3020470", | ||
"operation": "set", | ||
"generic": "no", | ||
"requestId": "DatasetRequest", | ||
"commands": [ | ||
{ | ||
"safReturnCode": 8, | ||
"returnCode": 16, | ||
"reasonCode": 4, | ||
"image": "ADDSD ('ESWIFT.TEST.T113622.P3020470')", | ||
"messages": [ | ||
"ICH09005I ESWIFT.TEST.T113622.P3020470 NOT FOUND IN CATALOG" | ||
] | ||
}, | ||
{ | ||
"safReturnCode": 8, | ||
"returnCode": 16, | ||
"reasonCode": 4, | ||
"image": "ALTDSD ('ESWIFT.TEST.T113622.P3020470') UACC (********)", | ||
"messages": [ | ||
"ICH22001I ESWIFT.TEST.T113622.P3020470 NOT DEFINED TO RACF" | ||
] | ||
} | ||
] | ||
}, | ||
"returnCode": 4, | ||
"reasonCode": 0, | ||
"runningUserid": "testuser" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"securityResult": { | ||
"dataSet": { | ||
"name": "ESWIFT.TEST.T1136242.P3020470", | ||
"operation": "set", | ||
"generic": "no", | ||
"requestId": "DatasetRequest", | ||
"info": [ | ||
"Definition exists. Add command skipped due to precheck option" | ||
], | ||
"commands": [ | ||
{ | ||
"safReturnCode": 0, | ||
"returnCode": 0, | ||
"reasonCode": 0, | ||
"image": "ALTDSD ('ESWIFT.TEST.T1136242.P3020470') UACC (Read) OWNER (********)" | ||
} | ||
] | ||
}, | ||
"returnCode": 0, | ||
"reasonCode": 0, | ||
"runningUserid": "testuser" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"securityResult": { | ||
"group": { | ||
"name": "TESTGRP0", | ||
"operation": "set", | ||
"requestId": "GroupRequest", | ||
"info": [ | ||
"Definition exists. Add command skipped due to precheck option" | ||
], | ||
"commands": [ | ||
{ | ||
"safReturnCode": 8, | ||
"returnCode": 16, | ||
"reasonCode": 8, | ||
"image": "ALTGROUP TESTGRP0 OMVS (GID (********))", | ||
"messages": [ | ||
"REDACTED MESSAGE CONCERNING GID, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION", | ||
"REDACTED MESSAGE CONCERNING GID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION", | ||
"REDACTED MESSAGE CONCERNING GID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION" | ||
] | ||
} | ||
] | ||
}, | ||
"returnCode": 4, | ||
"reasonCode": 0, | ||
"runningUserid": "testuser" | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.