Skip to content

feat: Add parser and spec for compliance_policies_enabled #4498

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

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

Conversation

wushiqinlou
Copy link
Contributor

@wushiqinlou wushiqinlou commented Jul 16, 2025

All Pull Requests:

Check all that apply:

  • Have you followed the guidelines in our Contributing document, including the instructions about commit messages?
  • No Sensitive Data in this change?
  • Is this PR to correct an issue?
  • Is this PR an enhancement?

Complete Description of Additions/Changes:

This parser is used to fix conflict between compliances and advisor rules.

Summary by Sourcery

Introduce a new datasource and parser to fetch and parse enabled compliance policies, and update existing datasources to respect compliance contexts.

New Features:

  • Add a compliance_advisor_rule_enabled datasource to retrieve enabled and tailoring policies via ComplianceClient
  • Add a JSON parser (ComplianceEnablePolicies) for the compliance_advisor_rule_enabled datasource

Enhancements:

  • Update os_version and package_check datasources to skip or exit based on compliance flags

Documentation:

  • Add documentation entry for compliance_enabled_policies parser

Tests:

  • Add tests for compliance_advisor_rule_enabled scenarios and updated os_version/package_check behavior
  • Add parser tests and doctests for ComplianceEnablePolicies

@xiangce xiangce marked this pull request as draft July 16, 2025 03:27
@codecov-commenter
Copy link

codecov-commenter commented Jul 17, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 77.68%. Comparing base (a75b1ca) to head (29d758c).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4498      +/-   ##
==========================================
+ Coverage   77.64%   77.68%   +0.03%     
==========================================
  Files         745      746       +1     
  Lines       41693    41730      +37     
  Branches     6703     6709       +6     
==========================================
+ Hits        32373    32417      +44     
+ Misses       8290     8284       -6     
+ Partials     1030     1029       -1     
Flag Coverage Δ
unittests 77.66% <100.00%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wushiqinlou wushiqinlou force-pushed the Add_parser_compliance_policies_enabled branch 2 times, most recently from aaa4497 to b727f14 Compare July 25, 2025 06:30
@wushiqinlou
Copy link
Contributor Author

@xiangce - please review this PR after merging #4503

@wushiqinlou wushiqinlou changed the title Add parser compliance_policies_enabled feat: Add parser compliance_policies_enabled Jul 25, 2025
@wushiqinlou wushiqinlou marked this pull request as ready for review July 25, 2025 06:56
@xiangce xiangce added data collection Changes that would impact the data collection data processing Changes that would impact the data processing/analyzing labels Jul 28, 2025
@xiangce
Copy link
Contributor

xiangce commented Aug 1, 2025

@sourcery-ai title

@xiangce
Copy link
Contributor

xiangce commented Aug 1, 2025

@sourcery-ai review

@sourcery-ai sourcery-ai bot changed the title feat: Add parser compliance_policies_enabled feat: Add compliance advisor rule enabled policies Aug 1, 2025
Copy link

sourcery-ai bot commented Aug 1, 2025

Reviewer's Guide

This PR introduces a new datasource to collect enabled compliance policies and optional tailoring rules, refactors existing datasources to conditionally skip when compliance flags are absent, registers the new datasource and corresponding parser in the Specs, and adds comprehensive tests and documentation.

ER diagram for compliance_enabled_policies data structure

erDiagram
    COMPLIANCE_ENABLED_POLICIES {
        enabled_policies json
        tailoring_policies json
    }
    ENABLED_POLICY {
        id string
        title string
        description string
        business_objective string
        compliance_threshold float
        total_system_count int
        type string
        os_major_version int
        profile_title string
        ref_id string
    }
    TAILORING_POLICY {
        ref_id string
    }
    CHECK_ITEM {
        idref string
        selected string
    }
    COMPLIANCE_ENABLED_POLICIES ||--o{ ENABLED_POLICY : contains
    COMPLIANCE_ENABLED_POLICIES ||--o{ TAILORING_POLICY : contains
    TAILORING_POLICY ||--o{ CHECK_ITEM : has
Loading

Class diagram for the new ComplianceEnablePolicies parser

classDiagram
    class ComplianceEnablePolicies {
        <<parser>>
        +parse(JSON data)
        +enabled_policies: list
        +tailoring_policies: list
    }
    ComplianceEnablePolicies --|> JSONParser
    JSONParser <|-- ComplianceEnablePolicies
    class JSONParser {
        <<base class>>
    }
Loading

File-Level Changes

Change Details Files
Add new datasource compliance_advisor_rule_enabled to fetch and assemble enabled policies and tailoring content into JSON
  • Created compliance_advisor_rule_enabled function with inputs os_version, package_check, HostContext
  • Used ComplianceClient to retrieve system policies and optional tailoring XML
  • Assembled result dict and returned via DatasourceProvider with JSON content and relative path
  • Handled missing policies or unexpected errors by raising SkipComponent
insights/specs/datasources/compliance/compliance_ds.py
Refactor os_version and package_check datasources to treat compliance flags as optional and skip non-compliance contexts
  • Changed datasource decorators to use optional parameter for compliance flags
  • Replaced unconditional sys.exit calls with conditional sys.exit or SkipComponent based on presence of compliance inputs
insights/specs/datasources/compliance/compliance_ds.py
Register the new compliance_advisor_rule_enabled datasource in the Specs framework
  • Added compliance_advisor_rule_enabled_policies RegistryPoint in Specs
  • Bound the new datasource in DefaultSpecs under the same registry point
insights/specs/__init__.py
insights/specs/default.py
Introduce ComplianceEnablePolicies parser for the new datasource output
  • Implemented JSONParser subclass ComplianceEnablePolicies with @parser decorator
  • Linked parser to Specs.compliance_advisor_rule_enabled_policies
insights/parsers/compliance_enabled_policies.py
Expand test suite to cover compliance-enabled policies and compliance flag behavior
  • Updated existing tests in test_compliance_ds.py to include compliance_enabled in brokers and SkipComponent cases
  • Added new datasource tests in test_compliance_ds.py for compliance_advisor_rule_enabled
  • Created parser tests in test_compliance_enabled_policies.py including doctest validation
insights/tests/datasources/compliance/test_compliance_ds.py
insights/tests/parsers/test_compliance_enabled_policies.py
Add documentation stub for the compliance_enabled_policies parser
  • Created reStructuredText file for shared parsers catalog entry
docs/shared_parsers_catalog/compliance_enabled_policies.rst

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @wushiqinlou - I've reviewed your changes - here's some feedback:

  • The optional lists in os_version and package_check reference compliance_policies_enabled, but the new spec is registered as compliance_advisor_rule_enabled_policies—please align the spec names across compliance_ds, Specs, DefaultSpecs, and the parser decorator.
  • The compliance_advisor_rule_enabled datasource wraps all exceptions and raises SkipComponent, which can obscure real errors—consider catching only the expected exceptions or letting others propagate for easier debugging.
  • The new tests include large inline XML/JSON blobs; extracting these into fixtures or external test resources would improve readability and maintainability.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `optional` lists in os_version and package_check reference `compliance_policies_enabled`, but the new spec is registered as `compliance_advisor_rule_enabled_policies`—please align the spec names across compliance_ds, Specs, DefaultSpecs, and the parser decorator.
- The compliance_advisor_rule_enabled datasource wraps all exceptions and raises SkipComponent, which can obscure real errors—consider catching only the expected exceptions or letting others propagate for easier debugging.
- The new tests include large inline XML/JSON blobs; extracting these into fixtures or external test resources would improve readability and maintainability.

## Individual Comments

### Comment 1
<location> `insights/tests/datasources/compliance/test_compliance_ds.py:454` </location>
<code_context>
+    compressor='gz',
+    compliance=False,
+)
+def test_compliance_advisor_rule_enabled_policies(config, policies, tailoring_content):
+    broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
+    ret = compliance_advisor_rule_enabled(broker)
+    assert ret.content == ['{"enabled_policies": [{"ref_id": "foo", "id": "def76af0-9b6f-4b37-ac6c-db61354acbb5"}], "tailoring_policies": [{"ref_id": "foo", "check_items": [{"idref": "xccdf_org.ssgproject.content_rule_bios_disable_usb_boot", "selected": "true"}, {"idref": "xccdf_org.ssgproject.content_rule_ssh_keys_passphrase_protected", "selected": "true"}]}]}']
+    assert ret.relative_path == "insights_datasources/compliance_enabled_policies"
+
+
</code_context>

<issue_to_address>
Missing test for malformed tailoring content in compliance_advisor_rule_enabled.

Add a test where fetch_tailoring_content returns malformed or invalid XML to verify that compliance_advisor_rule_enabled raises SkipComponent on parsing errors.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
+def test_compliance_advisor_rule_enabled_policies(config, policies, tailoring_content):
+    broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
+    ret = compliance_advisor_rule_enabled(broker)
+    assert ret.content == ['{"enabled_policies": [{"ref_id": "foo", "id": "def76af0-9b6f-4b37-ac6c-db61354acbb5"}], "tailoring_policies": [{"ref_id": "foo", "check_items": [{"idref": "xccdf_org.ssgproject.content_rule_bios_disable_usb_boot", "selected": "true"}, {"idref": "xccdf_org.ssgproject.content_rule_ssh_keys_passphrase_protected", "selected": "true"}]}]}']
+    assert ret.relative_path == "insights_datasources/compliance_enabled_policies"
+
+
=======
+def test_compliance_advisor_rule_enabled_policies(config, policies, tailoring_content):
+    broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
+    ret = compliance_advisor_rule_enabled(broker)
+    assert ret.content == ['{"enabled_policies": [{"ref_id": "foo", "id": "def76af0-9b6f-4b37-ac6c-db61354acbb5"}], "tailoring_policies": [{"ref_id": "foo", "check_items": [{"idref": "xccdf_org.ssgproject.content_rule_bios_disable_usb_boot", "selected": "true"}, {"idref": "xccdf_org.ssgproject.content_rule_ssh_keys_passphrase_protected", "selected": "true"}]}]}']
+    assert ret.relative_path == "insights_datasources/compliance_enabled_policies"
+
+
+# Test for malformed tailoring content
+import pytest
+from unittest.mock import patch
+
+def test_compliance_advisor_rule_enabled_malformed_tailoring(config, policies):
+    # Patch fetch_tailoring_content to return malformed XML
+    malformed_xml = "<Tailoring><Broken></Tailoring"  # missing closing '>'
+    with patch("insights.datasources.compliance.compliance_ds.fetch_tailoring_content", return_value=malformed_xml):
+        broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
+        with pytest.raises(SkipComponent):
+            compliance_advisor_rule_enabled(broker)
>>>>>>> REPLACE

</suggested_fix>

### Comment 2
<location> `insights/tests/datasources/compliance/test_compliance_ds.py:495` </location>
<code_context>
+    compressor='gz',
+    compliance=False,
+)
+def test_compliance_advisor_rule_enabled_policies_no_tailoring_policy(config, policies, tailoring_content):
+    broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
+    ret = compliance_advisor_rule_enabled(broker)
+    assert ret.content == ['{"enabled_policies": [{"ref_id": "foo", "id": "def76af0-9b6f-4b37-ac6c-db61354acbb5"}]}']
+    assert ret.relative_path == "insights_datasources/compliance_enabled_policies"
</code_context>

<issue_to_address>
Missing test for multiple policies with mixed tailoring content.

Please add a test where get_system_policies returns multiple policies, with and without tailoring content, to ensure tailoring_policies are included only when content is present.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
+    compressor='gz',
+    compliance=False,
+)
+def test_compliance_advisor_rule_enabled_policies_no_tailoring_policy(config, policies, tailoring_content):
+    broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
+    ret = compliance_advisor_rule_enabled(broker)
+    assert ret.content == ['{"enabled_policies": [{"ref_id": "foo", "id": "def76af0-9b6f-4b37-ac6c-db61354acbb5"}]}']
+    assert ret.relative_path == "insights_datasources/compliance_enabled_policies"
=======
+    compressor='gz',
+    compliance=False,
+)
+def test_compliance_advisor_rule_enabled_policies_no_tailoring_policy(config, policies, tailoring_content):
+    broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
+    ret = compliance_advisor_rule_enabled(broker)
+    assert ret.content == ['{"enabled_policies": [{"ref_id": "foo", "id": "def76af0-9b6f-4b37-ac6c-db61354acbb5"}]}']
+    assert ret.relative_path == "insights_datasources/compliance_enabled_policies"
+
+
+# Test for multiple policies with mixed tailoring content
+import json
+from unittest.mock import patch
+
+@patch(
+    "insights.specs.datasources.compliance.ComplianceClient.get_system_policies",
+)
+@patch(
+    "insights.specs.datasources.compliance.ComplianceClient.get_tailoring_content",
+)
+@patch(
+    "insights.client.config.InsightsConfig",
+    base_url='localhost/app',
+    systemid='',
+    proxy=None,
+    compressor='gz',
+    compliance=False,
+)
+def test_compliance_advisor_rule_enabled_policies_mixed_tailoring(
+    mock_config, mock_get_tailoring_content, mock_get_system_policies
+):
+    # Setup: two policies, one with tailoring, one without
+    policies = [
+        {'ref_id': 'foo', 'id': 'def76af0-9b6f-4b37-ac6c-db61354acbb5'},
+        {'ref_id': 'bar', 'id': 'abc12345-6789-4b37-ac6c-db61354acbb5'}
+    ]
+    tailoring_content = {
+        'def76af0-9b6f-4b37-ac6c-db61354acbb5': '{"tailoring": "content"}'
+        # No tailoring for 'abc12345-6789-4b37-ac6c-db61354acbb5'
+    }
+    mock_get_system_policies.return_value = policies
+    mock_get_tailoring_content.side_effect = lambda policy_id: tailoring_content.get(policy_id)
+    broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': mock_config}
+    ret = compliance_advisor_rule_enabled(broker)
+    # Parse the JSON output for easier assertions
+    result = json.loads(ret.content[0])
+    enabled_policies = result.get("enabled_policies", [])
+    tailoring_policies = result.get("tailoring_policies", [])
+    # Both policies should be enabled
+    assert {'ref_id': 'foo', 'id': 'def76af0-9b6f-4b37-ac6c-db61354acbb5'} in enabled_policies
+    assert {'ref_id': 'bar', 'id': 'abc12345-6789-4b37-ac6c-db61354acbb5'} in enabled_policies
+    # Only the policy with tailoring content should be in tailoring_policies
+    assert any(p['id'] == 'def76af0-9b6f-4b37-ac6c-db61354acbb5' for p in tailoring_policies)
+    assert all(p['id'] != 'abc12345-6789-4b37-ac6c-db61354acbb5' for p in tailoring_policies)
+    assert ret.relative_path == "insights_datasources/compliance_enabled_policies"
>>>>>>> REPLACE

</suggested_fix>

### Comment 3
<location> `insights/tests/parsers/test_compliance_enabled_policies.py:70` </location>
<code_context>
+    assert "Empty output." in str(ex)
+
+
+def test_compliance_enabled_policies():
+    compliance_enabled_policies_info = ComplianceEnablePolicies(context_wrap(COMPLIANCE_ENABLE_POLICIES))
+    assert compliance_enabled_policies_info['enabled_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_cis_server_l1'
+    assert compliance_enabled_policies_info['tailoring_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_cis_server_l1'
+    assert len(compliance_enabled_policies_info['enabled_policies']) == 2
+    assert len(compliance_enabled_policies_info['tailoring_policies'][0]['check_items']) == 4
+
+
</code_context>

<issue_to_address>
Missing test for parser with only enabled_policies and no tailoring_policies.

Add a test where the input JSON includes only enabled_policies and omits tailoring_policies to verify correct parser behavior when optional keys are missing.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
def test_compliance_enabled_policies():
    compliance_enabled_policies_info = ComplianceEnablePolicies(context_wrap(COMPLIANCE_ENABLE_POLICIES))
    assert compliance_enabled_policies_info['enabled_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_cis_server_l1'
    assert compliance_enabled_policies_info['tailoring_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_cis_server_l1'
    assert len(compliance_enabled_policies_info['enabled_policies']) == 2
    assert len(compliance_enabled_policies_info['tailoring_policies'][0]['check_items']) == 4
=======
def test_compliance_enabled_policies():
    compliance_enabled_policies_info = ComplianceEnablePolicies(context_wrap(COMPLIANCE_ENABLE_POLICIES))
    assert compliance_enabled_policies_info['enabled_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_cis_server_l1'
    assert compliance_enabled_policies_info['tailoring_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_cis_server_l1'
    assert len(compliance_enabled_policies_info['enabled_policies']) == 2
    assert len(compliance_enabled_policies_info['tailoring_policies'][0]['check_items']) == 4

def test_compliance_enabled_policies_only_enabled():
    """
    Test parser with only enabled_policies and no tailoring_policies.
    """
    only_enabled_policies = '''
    {
        "enabled_policies": [
            {
                "id": "12345678-aaaa-bbbb-cccc-1234567890ab",
                "ref_id": "xccdf_org.ssgproject.content_profile_standard"
            }
        ]
    }
    '''
    compliance_enabled_policies_info = ComplianceEnablePolicies(context_wrap(only_enabled_policies))
    assert 'enabled_policies' in compliance_enabled_policies_info
    assert compliance_enabled_policies_info['enabled_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_standard'
    assert 'tailoring_policies' not in compliance_enabled_policies_info or not compliance_enabled_policies_info.get('tailoring_policies')
>>>>>>> REPLACE

</suggested_fix>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 454 to 465
def test_compliance_advisor_rule_enabled_policies(config, policies, tailoring_content):
broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
ret = compliance_advisor_rule_enabled(broker)
assert ret.content == ['{"enabled_policies": [{"ref_id": "foo", "id": "def76af0-9b6f-4b37-ac6c-db61354acbb5"}], "tailoring_policies": [{"ref_id": "foo", "check_items": [{"idref": "xccdf_org.ssgproject.content_rule_bios_disable_usb_boot", "selected": "true"}, {"idref": "xccdf_org.ssgproject.content_rule_ssh_keys_passphrase_protected", "selected": "true"}]}]}']
assert ret.relative_path == "insights_datasources/compliance_enabled_policies"


Copy link

Choose a reason for hiding this comment

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

suggestion (testing): Missing test for malformed tailoring content in compliance_advisor_rule_enabled.

Add a test where fetch_tailoring_content returns malformed or invalid XML to verify that compliance_advisor_rule_enabled raises SkipComponent on parsing errors.

Suggested change
def test_compliance_advisor_rule_enabled_policies(config, policies, tailoring_content):
broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
ret = compliance_advisor_rule_enabled(broker)
assert ret.content == ['{"enabled_policies": [{"ref_id": "foo", "id": "def76af0-9b6f-4b37-ac6c-db61354acbb5"}], "tailoring_policies": [{"ref_id": "foo", "check_items": [{"idref": "xccdf_org.ssgproject.content_rule_bios_disable_usb_boot", "selected": "true"}, {"idref": "xccdf_org.ssgproject.content_rule_ssh_keys_passphrase_protected", "selected": "true"}]}]}']
assert ret.relative_path == "insights_datasources/compliance_enabled_policies"
+def test_compliance_advisor_rule_enabled_policies(config, policies, tailoring_content):
+ broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
+ ret = compliance_advisor_rule_enabled(broker)
+ assert ret.content == ['{"enabled_policies": [{"ref_id": "foo", "id": "def76af0-9b6f-4b37-ac6c-db61354acbb5"}], "tailoring_policies": [{"ref_id": "foo", "check_items": [{"idref": "xccdf_org.ssgproject.content_rule_bios_disable_usb_boot", "selected": "true"}, {"idref": "xccdf_org.ssgproject.content_rule_ssh_keys_passphrase_protected", "selected": "true"}]}]}']
+ assert ret.relative_path == "insights_datasources/compliance_enabled_policies"
+
+
+# Test for malformed tailoring content
+import pytest
+from unittest.mock import patch
+
+def test_compliance_advisor_rule_enabled_malformed_tailoring(config, policies):
+ # Patch fetch_tailoring_content to return malformed XML
+ malformed_xml = "<Tailoring><Broken></Tailoring" # missing closing '>'
+ with patch("insights.datasources.compliance.compliance_ds.fetch_tailoring_content", return_value=malformed_xml):
+ broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
+ with pytest.raises(SkipComponent):
+ compliance_advisor_rule_enabled(broker)

Copy link
Contributor

Choose a reason for hiding this comment

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

Please consider this suggestion from sourcery-ai.

Comment on lines 492 to 568
compressor='gz',
compliance=False,
)
def test_compliance_advisor_rule_enabled_policies_no_tailoring_policy(config, policies, tailoring_content):
broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
ret = compliance_advisor_rule_enabled(broker)
assert ret.content == ['{"enabled_policies": [{"ref_id": "foo", "id": "def76af0-9b6f-4b37-ac6c-db61354acbb5"}]}']
assert ret.relative_path == "insights_datasources/compliance_enabled_policies"
Copy link

Choose a reason for hiding this comment

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

suggestion (testing): Missing test for multiple policies with mixed tailoring content.

Please add a test where get_system_policies returns multiple policies, with and without tailoring content, to ensure tailoring_policies are included only when content is present.

Suggested change
compressor='gz',
compliance=False,
)
def test_compliance_advisor_rule_enabled_policies_no_tailoring_policy(config, policies, tailoring_content):
broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
ret = compliance_advisor_rule_enabled(broker)
assert ret.content == ['{"enabled_policies": [{"ref_id": "foo", "id": "def76af0-9b6f-4b37-ac6c-db61354acbb5"}]}']
assert ret.relative_path == "insights_datasources/compliance_enabled_policies"
+ compressor='gz',
+ compliance=False,
+)
+def test_compliance_advisor_rule_enabled_policies_no_tailoring_policy(config, policies, tailoring_content):
+ broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
+ ret = compliance_advisor_rule_enabled(broker)
+ assert ret.content == ['{"enabled_policies": [{"ref_id": "foo", "id": "def76af0-9b6f-4b37-ac6c-db61354acbb5"}]}']
+ assert ret.relative_path == "insights_datasources/compliance_enabled_policies"
+
+
+# Test for multiple policies with mixed tailoring content
+import json
+from unittest.mock import patch
+
+@patch(
+ "insights.specs.datasources.compliance.ComplianceClient.get_system_policies",
+)
+@patch(
+ "insights.specs.datasources.compliance.ComplianceClient.get_tailoring_content",
+)
+@patch(
+ "insights.client.config.InsightsConfig",
+ base_url='localhost/app',
+ systemid='',
+ proxy=None,
+ compressor='gz',
+ compliance=False,
+)
+def test_compliance_advisor_rule_enabled_policies_mixed_tailoring(
+ mock_config, mock_get_tailoring_content, mock_get_system_policies
+):
+ # Setup: two policies, one with tailoring, one without
+ policies = [
+ {'ref_id': 'foo', 'id': 'def76af0-9b6f-4b37-ac6c-db61354acbb5'},
+ {'ref_id': 'bar', 'id': 'abc12345-6789-4b37-ac6c-db61354acbb5'}
+ ]
+ tailoring_content = {
+ 'def76af0-9b6f-4b37-ac6c-db61354acbb5': '{"tailoring": "content"}'
+ # No tailoring for 'abc12345-6789-4b37-ac6c-db61354acbb5'
+ }
+ mock_get_system_policies.return_value = policies
+ mock_get_tailoring_content.side_effect = lambda policy_id: tailoring_content.get(policy_id)
+ broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': mock_config}
+ ret = compliance_advisor_rule_enabled(broker)
+ # Parse the JSON output for easier assertions
+ result = json.loads(ret.content[0])
+ enabled_policies = result.get("enabled_policies", [])
+ tailoring_policies = result.get("tailoring_policies", [])
+ # Both policies should be enabled
+ assert {'ref_id': 'foo', 'id': 'def76af0-9b6f-4b37-ac6c-db61354acbb5'} in enabled_policies
+ assert {'ref_id': 'bar', 'id': 'abc12345-6789-4b37-ac6c-db61354acbb5'} in enabled_policies
+ # Only the policy with tailoring content should be in tailoring_policies
+ assert any(p['id'] == 'def76af0-9b6f-4b37-ac6c-db61354acbb5' for p in tailoring_policies)
+ assert all(p['id'] != 'abc12345-6789-4b37-ac6c-db61354acbb5' for p in tailoring_policies)
+ assert ret.relative_path == "insights_datasources/compliance_enabled_policies"

Copy link
Contributor

Choose a reason for hiding this comment

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

Please consider this suggestion from sourcery-ai.

Comment on lines 70 to 75
def test_compliance_enabled_policies():
compliance_enabled_policies_info = ComplianceEnablePolicies(context_wrap(COMPLIANCE_ENABLE_POLICIES))
assert compliance_enabled_policies_info['enabled_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_cis_server_l1'
assert compliance_enabled_policies_info['tailoring_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_cis_server_l1'
assert len(compliance_enabled_policies_info['enabled_policies']) == 2
assert len(compliance_enabled_policies_info['tailoring_policies'][0]['check_items']) == 4
Copy link

Choose a reason for hiding this comment

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

suggestion (testing): Missing test for parser with only enabled_policies and no tailoring_policies.

Add a test where the input JSON includes only enabled_policies and omits tailoring_policies to verify correct parser behavior when optional keys are missing.

Suggested change
def test_compliance_enabled_policies():
compliance_enabled_policies_info = ComplianceEnablePolicies(context_wrap(COMPLIANCE_ENABLE_POLICIES))
assert compliance_enabled_policies_info['enabled_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_cis_server_l1'
assert compliance_enabled_policies_info['tailoring_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_cis_server_l1'
assert len(compliance_enabled_policies_info['enabled_policies']) == 2
assert len(compliance_enabled_policies_info['tailoring_policies'][0]['check_items']) == 4
def test_compliance_enabled_policies():
compliance_enabled_policies_info = ComplianceEnablePolicies(context_wrap(COMPLIANCE_ENABLE_POLICIES))
assert compliance_enabled_policies_info['enabled_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_cis_server_l1'
assert compliance_enabled_policies_info['tailoring_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_cis_server_l1'
assert len(compliance_enabled_policies_info['enabled_policies']) == 2
assert len(compliance_enabled_policies_info['tailoring_policies'][0]['check_items']) == 4
def test_compliance_enabled_policies_only_enabled():
"""
Test parser with only enabled_policies and no tailoring_policies.
"""
only_enabled_policies = '''
{
"enabled_policies": [
{
"id": "12345678-aaaa-bbbb-cccc-1234567890ab",
"ref_id": "xccdf_org.ssgproject.content_profile_standard"
}
]
}
'''
compliance_enabled_policies_info = ComplianceEnablePolicies(context_wrap(only_enabled_policies))
assert 'enabled_policies' in compliance_enabled_policies_info
assert compliance_enabled_policies_info['enabled_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_standard'
assert 'tailoring_policies' not in compliance_enabled_policies_info or not compliance_enabled_policies_info.get('tailoring_policies')

Copy link
Contributor

Choose a reason for hiding this comment

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

Please consider this suggestion from sourcery-ai.


@datasource(os_version, package_check, HostContext, timeout=0)
def compliance_advisor_rule_enabled(broker):
try:
Copy link

Choose a reason for hiding this comment

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

issue (code-quality): We've found these issues:

@xiangce xiangce changed the title feat: Add compliance advisor rule enabled policies feat: Add parser for compliance advisor rule enabled policies Aug 1, 2025
{
"enabled_policies": [
{
"id": "717539de-3c90-473b-acca-c8ee95bb6cc3",
Copy link
Contributor

Choose a reason for hiding this comment

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

Please mock/obfuscate these ids to avoid misunderstanding

"ref_id": "xccdf_org.ssgproject.content_profile_cis_server_l1"
},
{
"id": "bc11fd8a-9c76-484c-ac63-14b29414a455",
Copy link
Contributor

Choose a reason for hiding this comment

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

Please mock/obfuscate these ids to avoid misunderstanding

@@ -115,6 +115,7 @@ class DefaultSpecs(Specs):
compliance_policies = compliance_ds.compliance_policies
compliance_assign = compliance_ds.compliance_assign
compliance_unassign = compliance_ds.compliance_unassign
compliance_advisor_rule_enabled_policies = compliance_ds.compliance_advisor_rule_enabled
Copy link
Contributor

Choose a reason for hiding this comment

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

As this is not a spec belong to the --compliance family, please move it to the next section.

)
@patch(
"insights.specs.datasources.compliance.ComplianceClient.get_system_policies",
return_value=[{'ref_id': 'foo', 'id': 'def76af0-9b6f-4b37-ac6c-db61354acbb5'}],
Copy link
Contributor

Choose a reason for hiding this comment

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

Please mock/obfuscate these ids to avoid misunderstanding

tailoring_policy['check_items'].append(item.attrib)
result['tailoring_policies'].append(tailoring_policy)
if not result['tailoring_policies']:
del result['tailoring_policies']
Copy link
Contributor

Choose a reason for hiding this comment

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

How about:

tailoring_policies = []


for ...
    ...
    tailoring_policies.append(tailoring_policy)
if tailoring_policies:
    result['tailoring_policies'] = tailoring_policies

instead of adding it then remove it when empty?

profile_select_tag = pre_tag + 'Profile/' + pre_tag + 'select'
profile_select_info = xml_root.findall(profile_select_tag)
for item in profile_select_info:
tailoring_policy['check_items'].append(item.attrib)
Copy link
Contributor

Choose a reason for hiding this comment

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

How about

tailoring_policy['check_items'] = [item.attrib for item in profile_select_info]

@@ -17,6 +17,7 @@ class Specs(SpecSet):
compliance_policies = RegistryPoint()
compliance_assign = RegistryPoint()
compliance_unassign = RegistryPoint()
compliance_advisor_rule_enabled_policies = RegistryPoint()
Copy link
Contributor

Choose a reason for hiding this comment

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

Better move it the next Regular specs section.

Comment on lines 454 to 465
def test_compliance_advisor_rule_enabled_policies(config, policies, tailoring_content):
broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
ret = compliance_advisor_rule_enabled(broker)
assert ret.content == ['{"enabled_policies": [{"ref_id": "foo", "id": "def76af0-9b6f-4b37-ac6c-db61354acbb5"}], "tailoring_policies": [{"ref_id": "foo", "check_items": [{"idref": "xccdf_org.ssgproject.content_rule_bios_disable_usb_boot", "selected": "true"}, {"idref": "xccdf_org.ssgproject.content_rule_ssh_keys_passphrase_protected", "selected": "true"}]}]}']
assert ret.relative_path == "insights_datasources/compliance_enabled_policies"


Copy link
Contributor

Choose a reason for hiding this comment

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

Please consider this suggestion from sourcery-ai.

Comment on lines 492 to 568
compressor='gz',
compliance=False,
)
def test_compliance_advisor_rule_enabled_policies_no_tailoring_policy(config, policies, tailoring_content):
broker = {os_version: ['8', '10'], package_check: '0.1.73', 'client_config': config}
ret = compliance_advisor_rule_enabled(broker)
assert ret.content == ['{"enabled_policies": [{"ref_id": "foo", "id": "def76af0-9b6f-4b37-ac6c-db61354acbb5"}]}']
assert ret.relative_path == "insights_datasources/compliance_enabled_policies"
Copy link
Contributor

Choose a reason for hiding this comment

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

Please consider this suggestion from sourcery-ai.

Comment on lines 70 to 75
def test_compliance_enabled_policies():
compliance_enabled_policies_info = ComplianceEnablePolicies(context_wrap(COMPLIANCE_ENABLE_POLICIES))
assert compliance_enabled_policies_info['enabled_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_cis_server_l1'
assert compliance_enabled_policies_info['tailoring_policies'][0]['ref_id'] == 'xccdf_org.ssgproject.content_profile_cis_server_l1'
assert len(compliance_enabled_policies_info['enabled_policies']) == 2
assert len(compliance_enabled_policies_info['tailoring_policies'][0]['check_items']) == 4
Copy link
Contributor

Choose a reason for hiding this comment

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

Please consider this suggestion from sourcery-ai.

@xiangce xiangce changed the title feat: Add parser for compliance advisor rule enabled policies feat: Add parser and spec for compliance_policies_enabled Aug 1, 2025
@@ -0,0 +1,78 @@
"""
ComplianceEnablePolicies - datasource ``compliance_advisor_rule_enabled``
Copy link
Contributor

Choose a reason for hiding this comment

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

Please update the data source/spec/parser name a bit consistent.

@wushiqinlou wushiqinlou force-pushed the Add_parser_compliance_policies_enabled branch 4 times, most recently from 35152ad to 625fe2f Compare August 5, 2025 09:42
@wushiqinlou
Copy link
Contributor Author

@xiangce - updated per your comments, please confirm again.

Signed-off-by: Jiajun Zhang <[email protected]>
Signed-off-by: Jiajun Zhang <[email protected]>
Signed-off-by: Jiajun Zhang <[email protected]>
Signed-off-by: Jiajun Zhang <[email protected]>
Signed-off-by: Jiajun Zhang <[email protected]>
Signed-off-by: Jiajun Zhang <[email protected]>
Signed-off-by: Jiajun Zhang <[email protected]>
Signed-off-by: Jiajun Zhang <[email protected]>
Signed-off-by: Jiajun Zhang <[email protected]>
Signed-off-by: Jiajun Zhang <[email protected]>
Signed-off-by: Jiajun Zhang <[email protected]>
@wushiqinlou wushiqinlou force-pushed the Add_parser_compliance_policies_enabled branch from 9a3be71 to 29d758c Compare August 6, 2025 02:17
@xiangce xiangce added the QE label Aug 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
data collection Changes that would impact the data collection data processing Changes that would impact the data processing/analyzing QE
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants