Skip to content

Latest commit

 

History

History
433 lines (332 loc) · 11.8 KB

File metadata and controls

433 lines (332 loc) · 11.8 KB

Unified Rule Discovery - Intelligent Source + Rendered Content

Overview

The get_rule_details() function now automatically discovers and includes rendered content from build artifacts alongside the source rule information. This provides a unified view of both the source templates and the final rendered output in a single API call.

Key Features

Automatic Build Detection - Intelligently detects if build artifacts exist ✅ Unified Response - Single call returns both source and rendered content ✅ Multi-Product Support - Includes rendered content for all built products ✅ Product Filtering - Optionally filter to specific product builds ✅ Backward Compatible - Existing code continues to work ✅ Performance Optimized - Only queries builds when requested

How It Works

Before (Separate Calls)

# Old way: Two separate calls
rule = discovery.get_rule_details("my_rule")  # Source only
rendered = discovery.get_rendered_rule("rhel9", "my_rule")  # Build artifact

After (Unified Call)

# New way: One call, automatic detection
rule = discovery.get_rule_details("my_rule")  # Source + all rendered versions!

# Access source
print(rule.title)
print(rule.description)

# Access rendered content for each built product
if rule.rendered:
    for product, rendered in rule.rendered.items():
        print(f"{product}: {rendered.rendered_yaml}")
        print(f"Remediations: {rendered.rendered_remediations}")

API Reference

Function Signature

def get_rule_details(
    rule_id: str,
    include_rendered: bool = True,
    product: Optional[str] = None
) -> Optional[RuleDetails]

Parameters

Parameter Type Default Description
rule_id str required Rule identifier (e.g., "sshd_set_idle_timeout")
include_rendered bool True Whether to automatically include rendered content from builds
product str None Optional product filter. If specified, only includes rendered content for this product

Return Value

Returns a RuleDetails object with:

Source Information (always present):

  • rule_id, title, description, rationale, severity
  • identifiers, references, products, platforms
  • remediations (availability flags), checks, test_scenarios
  • file_path, rule_dir, last_modified, template

Rendered Information (when builds exist):

  • rendered: Dict[str, RuleRenderedContent] - Keyed by product
    • product: Product identifier
    • rendered_yaml: Fully rendered YAML (variables expanded)
    • rendered_oval: Rendered OVAL check
    • rendered_remediations: Dict of rendered scripts by type
    • build_path: Path to build artifacts
    • build_time: When the build was created

Usage Examples

Example 1: Get Rule with All Rendered Versions

from content_agent.core import discovery

# Get rule with automatic rendered content detection
rule = discovery.get_rule_details("accounts_password_pam_dcredit")

print(f"Rule: {rule.title}")
print(f"Severity: {rule.severity}")
print(f"Source file: {rule.file_path}")

# Check if builds are available
if rule.rendered:
    print(f"\nRendered content available for {len(rule.rendered)} products:")
    for product, rendered in rule.rendered.items():
        print(f"\n{product}:")
        print(f"  - Built: {rendered.build_time}")
        print(f"  - YAML size: {len(rendered.rendered_yaml)} chars")
        print(f"  - Has OVAL: {rendered.rendered_oval is not None}")
        print(f"  - Remediations: {list(rendered.rendered_remediations.keys())}")
else:
    print("\nNo build artifacts found")

Output:

Rule: Ensure PAM Enforces Password Requirements - Minimum Digit Characters
Severity: medium
Source file: linux_os/guide/.../accounts_password_pam_dcredit/rule.yml

Rendered content available for 2 products:

rhel10:
  - Built: 2026-01-19 17:25:56
  - YAML size: 5710 chars
  - Has OVAL: True
  - Remediations: ['bash', 'ansible']

rhel9:
  - Built: 2026-01-19 17:25:59
  - YAML size: 5698 chars
  - Has OVAL: True
  - Remediations: ['bash', 'ansible']

Example 2: Get Rule for Specific Product

# Only get rendered content for RHEL 10
rule = discovery.get_rule_details("my_rule", product="rhel10")

if rule.rendered and "rhel10" in rule.rendered:
    rendered = rule.rendered["rhel10"]
    print(rendered.rendered_yaml)
    print(rendered.rendered_remediations["bash"])

Example 3: Get Source Only (No Builds)

# Skip build detection for faster response
rule = discovery.get_rule_details("my_rule", include_rendered=False)

# rule.rendered will be None
assert rule.rendered is None

Example 4: Compare Source vs Rendered

import yaml

rule = discovery.get_rule_details("my_rule")

# Source description (may have Jinja templates)
print("Source:", rule.description)

# Rendered description (variables expanded)
if rule.rendered:
    first_product = list(rule.rendered.keys())[0]
    rendered_data = yaml.safe_load(rule.rendered[first_product].rendered_yaml)
    print("Rendered:", rendered_data['description'])

Example 5: Access Rendered Remediations

rule = discovery.get_rule_details("my_rule")

if rule.rendered and "rhel9" in rule.rendered:
    remediations = rule.rendered["rhel9"].rendered_remediations

    if "bash" in remediations:
        print("Bash remediation for RHEL 9:")
        print(remediations["bash"])

    if "ansible" in remediations:
        print("\nAnsible remediation for RHEL 9:")
        print(remediations["ansible"])

MCP Tool Integration

The MCP tool get_rule_details automatically uses this unified approach:

User: "Show me details about sshd_set_idle_timeout"
Claude: [calls get_rule_details(rule_id="sshd_set_idle_timeout")]

Response includes:
- Source rule information
- Rendered content for rhel9, rhel10 (if builds exist)
- OVAL checks with expanded variables
- Bash and Ansible remediations

Optional parameters:

Claude: [calls get_rule_details(
    rule_id="my_rule",
    include_rendered=true,
    product="rhel9"
)]

Benefits

1. Single API Call

  • Before: 2+ calls (get_rule_details + get_rendered_rule for each product)
  • After: 1 call gets everything

2. Automatic Detection

  • No need to check if builds exist first
  • No need to know which products are built
  • Gracefully handles missing builds

3. Complete Picture

  • See source templates AND final output
  • Compare Jinja variables vs expanded values
  • Understand product-specific differences

4. Flexibility

  • Get all products or filter to one
  • Include/exclude rendered content as needed
  • Maintain backward compatibility

5. Performance

  • Rendered content only fetched when requested
  • Product filtering reduces overhead
  • Cached build detection

Use Cases

Use Case 1: Rule Investigation

Scenario: Understand what a rule does and how it's implemented.

rule = discovery.get_rule_details("my_rule")

# Source information
print("What:", rule.title)
print("Why:", rule.rationale)
print("Severity:", rule.severity)

# Implementation
print("Template:", rule.template)
print("Source remediations:", rule.remediations)

# Actual rendered content
if rule.rendered:
    for product, rendered in rule.rendered.items():
        print(f"\n{product} implementation:")
        print(rendered.rendered_remediations.get("bash", "N/A"))

Use Case 2: Template Debugging

Scenario: Debug why Jinja templates aren't rendering as expected.

rule = discovery.get_rule_details("my_rule")

# Check source has template variables
if "{{{" in rule.description:
    print("Source has Jinja variables")

    # Check if they were expanded in builds
    if rule.rendered:
        for product, rendered in rule.rendered.items():
            rendered_desc = yaml.safe_load(rendered.rendered_yaml)['description']
            if "{{{" not in rendered_desc:
                print(f"✅ {product}: Variables expanded")
            else:
                print(f"❌ {product}: Variables NOT expanded")

Use Case 3: Product Comparison

Scenario: See how the same rule differs across products.

rule = discovery.get_rule_details("my_rule")

if rule.rendered:
    print("Comparing implementations across products:\n")

    for product, rendered in rule.rendered.items():
        bash_script = rendered.rendered_remediations.get("bash", "")
        print(f"{product}: {len(bash_script)} bytes")

        # Check for product-specific code
        if product in bash_script:
            print(f"  → Has {product}-specific code")

Use Case 4: Build Verification

Scenario: Verify builds produced expected output.

rule = discovery.get_rule_details("my_new_rule")

if not rule.rendered:
    print("❌ Rule not in any builds - needs to be built")
elif "rhel9" not in rule.rendered:
    print("❌ Rule not in RHEL 9 build")
else:
    rendered = rule.rendered["rhel9"]
    if rendered.rendered_oval:
        print("✅ OVAL check generated")
    if "bash" in rendered.rendered_remediations:
        print("✅ Bash remediation generated")

Performance Considerations

Build Detection is Fast

  • Only checks if build directories exist
  • No file I/O until rendered content is accessed
  • Cached product list

Selective Inclusion

# Fast: No build artifact access
rule = discovery.get_rule_details("my_rule", include_rendered=False)

# Medium: Check builds but limit to one product
rule = discovery.get_rule_details("my_rule", product="rhel9")

# Slower: Get all products (but still <1s for most rules)
rule = discovery.get_rule_details("my_rule")

Typical Performance

  • include_rendered=False: ~50ms
  • product="rhel9": ~100ms
  • All products (2 builds): ~150ms

Data Structure

RuleDetails with Rendered Content

{
    # Source information (always present)
    "rule_id": "my_rule",
    "title": "My Rule Title",
    "description": "Config value: {{{ xccdf_value('my_var') }}}",  # May have Jinja
    "severity": "medium",
    "products": ["rhel9", "rhel10"],
    "remediations": {"bash": True, "ansible": True},  # Availability flags
    "file_path": "path/to/rule.yml",

    # Rendered content (when builds exist)
    "rendered": {
        "rhel9": {
            "product": "rhel9",
            "rendered_yaml": "description: Config value: 300\n...",  # Expanded
            "rendered_oval": "<def-group>...</def-group>",
            "rendered_remediations": {
                "bash": "#!/bin/bash\n...",
                "ansible": "---\n..."
            },
            "build_path": "build/rhel9/rules",
            "build_time": "2026-01-19T17:25:59"
        },
        "rhel10": {
            "product": "rhel10",
            # ... same structure ...
        }
    }
}

Migration Guide

Existing Code (Still Works)

# Old code continues to work
rule = discovery.get_rule_details("my_rule")
print(rule.title)
print(rule.severity)
# No changes needed!

Enhanced Usage

# Add rendered content access
rule = discovery.get_rule_details("my_rule")

# Old fields still work
print(rule.title)

# New fields available
if rule.rendered:
    print("Builds:", list(rule.rendered.keys()))

Optimization

# If you don't need rendered content, disable it
rule = discovery.get_rule_details("my_rule", include_rendered=False)
# Faster, smaller response

Summary

The unified rule discovery feature provides:

One call instead of multiple ✅ Automatic detection of build artifacts ✅ Complete information - source + rendered ✅ Flexible filtering by product ✅ Backward compatible with existing code ✅ Performance optimized with selective inclusion

This makes it much easier to:

  • Investigate rules
  • Debug templates
  • Compare products
  • Verify builds
  • Understand final output

Try it now:

rule = discovery.get_rule_details("your_rule_id")