Skip to content
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

Feature/additional secrets improvements #80

Open
wants to merge 14 commits into
base: dev
Choose a base branch
from

Conversation

ElijahSwiftIBM
Copy link
Collaborator

💡 Issue Reference

Issue: #68

💻 What does this address?

Additional Secrets Redaction was marked experimental due to 2 key weaknesses. 1) not redacting secret material in messages in the security response object and 2) the method of secret redaction opened the possibility of incomplete redaction of "additional" secrets.

📟 Implementation Details

Additional Secrets redaction now uses more streamlined regex pattern matching to exhaustively redact secrets in the command images. Additional features were added to redact messages by their RACF tag to ensure that no "false positives" would give away redacted fields.

📋 Is there a test case?

Additional test cases were added/existing test cases were changed to test the new behavior of redacting more complex and varied fields as well as messages.

-Add code to redact messages that may contain additionally redacted secrets based on the `racf_key`.
-Update unit testing to account for this

Signed-off-by: Elijah Swift <elijah.swift@ibm.com>
Signed-off-by: Elijah Swift <elijah.swift@ibm.com>
-Update redaction logic for request and result xml data.
-Check for Value () or Value ('') (the single quotes are for full strings that can contain "delimiters" like () otherwise this would mess with IKJPARSE. This works well for our logic too) patterns and use non-greedy regular expressions to replace the values specified in the patterns
-This passes all existing unit tests. New unit tests will be built for possible commands not seen (would have failed on previous logic)

Signed-off-by: Elijah Swift <elijah.swift@ibm.com>
Added a unit test to verify installation data can be redacted when contains characters like () (on requests)

Signed-off-by: Elijah Swift <elijah.swift@ibm.com>
Signed-off-by: Elijah Swift <elijah.swift@ibm.com>
rf"<message>REDACTED MESSAGE CONCERNING {racf_key.upper()}, "
+ r"REVIEW DOCUMENTATION OF \1 FOR MORE INFORMATION</message>",
security_result,
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also explain how these regexes work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the following:

    This function employs the following regular expression:
    <message>([A-Z]*[0-9]*[A-Z]) [^<>]*{racf_key.upper()}[^<>]*<\/message>" - 
    Designed to match the above pattern by starting and ending with the message xml tag 
    string as shown, the value of the message is targeted based on the racf_key. This
    should capture only messages that contain information about a redacted key.
    The message identifier is "captured" as a variable and preserved by the substitution
    operation through the use of the \1 supplied in the replacement string.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a slight working change suggestion:

Designed to match any message xml element that contains the provided racf_key.

Instead of

Designed to match the above pattern by starting and ending with the message xml tag string as shown, the value of the message is targeted based on the racf_key.

"base:installation_data"
],
result,
)
Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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".

Copy link
Member

@lcarcaramo lcarcaramo Mar 6, 2024

Choose a reason for hiding this comment

The 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?

-Move Secrets Redaction Unit Testing To Common
-Generalize Secrets Redaction Unit testing to other applicable function groups
-Add mapping in logger functions for RACF Keys that do not match values in the trait dictionary
-Add mapping in logger functions for RACF Message keys that do not match the value in the RACF key

Signed-off-by: Elijah Swift <elijah.swift@ibm.com>
-Unit Test for Redacting traits that map based on logger dictionary (Audit rules)
-Unit test for traits that have different message strings (UACC)

Signed-off-by: Elijah Swift <elijah.swift@ibm.com>
-Remove Experimental Tag for logging of additional secret traits
-Update lots of docstrings

Signed-off-by: Elijah Swift <elijah.swift@ibm.com>
-Add back experimental tag (likely true at release)
-Trim down Regex comments
-Rearchitect racf_key_map and racf_message_key_map to belong to individual administration objects
-Move Unit test samples used ONLY for additional secrets testing to Common

Signed-off-by: Elijah Swift <elijah.swift@ibm.com>
)

@patch("pyracf.common.irrsmo00.IRRSMO00.call_racf")
def test_resource_admin_custom_mapped_secret_redacted_on_success(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to test an error scenario for audit rules?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, the redaction of secrets is functionally no different on success cases or error cases with the potential exception of messages concerning the field. If you would like me to add the tests as well, I can, but they don't take us through any additional code paths.

Copy link
Member

@lcarcaramo lcarcaramo Mar 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you get error messages with audit rules when there is a failure, it might make sense to add a test case for that. I guess I will leave that up to you though since none of the traits are intended to be fully stabilized in this release.

-Moved Debug logging testing of secret traits to common
-Re-architected racf_key_map and racf_message_key_map under 1 dictionary
-changed comments
-established SecretRedactionError

Signed-off-by: Elijah Swift <elijah.swift@ibm.com>
Change Secrets Redaction Error message for invalid admin function

Signed-off-by: Elijah Swift <elijah.swift@ibm.com>
@ElijahSwiftIBM
Copy link
Collaborator Author

Dealt with many of the comments here, but there is still more to do.

  • SecretsRedactionError needs unit testing now that it exists
  • More debug logging unit tests for the other admin functions

I will deal with these next week, but the rest is ready for review.

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 open and close parentheses rather than a direct
seqence of them. The pattern looks for these nested open parenthesis as a sequence would
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wording of the last couple sentences here is still a little confusing to me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed to the following, please let me know if this is still unclear:

    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'(' before the ')'. This indicates that there is a "nested"
    structure rather than a sequential one. In the example, this portion of the pattern
    matches '( subtrait1(', but would not match '(value) subtrait2(' because of the ')'
    character between the two '(' characters. 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 to handle all possible trait values and subtrait names
    followed by the "end pattern" of ')' and ')' separated by a variable (potentially zero)
    whitespace.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is ok now. There is just a typo that I think needs to be fixed:

change

a'(' before the ')'.

to

a '(' before the ')'.

-Change error type to ValueError
-Address typos and comment fixes
-Ensure AssertNotIn tests use proper strings

Signed-off-by: Elijah Swift <elijah.swift@ibm.com>
Add Unit Testing for ValueError in Secrets Redaction
Tweak ValueError Text

Signed-off-by: Elijah Swift <elijah.swift@ibm.com>
Add debug logging redaction unit testing for other function groups
Fix some underlying bugs/inconsistencies in other unit testing.

Signed-off-by: Elijah Swift <elijah.swift@ibm.com>
@lcarcaramo lcarcaramo added the enhancement New feature or request label Mar 6, 2024
@lcarcaramo lcarcaramo added this to the Beta 1.0b6 milestone Mar 6, 2024
for secret in additional_secret_traits:
if secret in self.__secret_traits:
continue
if ":" not in secret:
bad_secret_traits.append(
f"\nCould not map {secret} to a valid segment trait."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is sufficient to say "\n'{secret}' is not a valid trait."


# ============================================================================
# Secrets Redaction
# ============================================================================
def __add_additional_secret_traits(self, additional_secret_traits: list) -> None:
"""Add additional fields to be redacted in logger output."""
unsupported_profile_types = ["permission", "groupConnection", "systemSettings"]
error_message = (
f"Cannot add specified additional secrets to '{self._profile_type}' object."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to use inspect.stack() here to get the name of the "admin" class (i.e., UserAdmin):

admin_class = "".join(class_tokens)

Also change the word object to type and change Cannot to Unable.

You should get an exception message like this when you are done:

Unable to add specified additional secrets to 'UserAdmin' type.

Make similar corresponding changes to the unsupported profile type exception message.

else:
match = re.search(rf"{racf_key.upper()} +\(", security_result)
match = re.search(rf"{racf_key.upper()}( +){{0,}}\(", security_result)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

match = re.search(racf_command_argument_regex, security_result)

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_trait_and_message_key_map.get("message_map", {}):
Copy link
Member

@lcarcaramo lcarcaramo Mar 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments to indicate which stage of processing we are at? Looks like there are two steps. Redact the "RACF command" and redact any corresponding "RACF messages"

@@ -35,7 +35,7 @@ def test_alter_group_request_debug_log_works_on_success(
stdout = io.StringIO()
with contextlib.redirect_stdout(stdout):
self.group_admin.alter(
"TESTGRP0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS
"testgrp0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the nature of these changes?

@lcarcaramo lcarcaramo removed this from the Beta 1.0b6 milestone Sep 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants