From 838aba33a67d83ba4028014e8e7497f6290ee4f4 Mon Sep 17 00:00:00 2001 From: Vojtech Polasek Date: Wed, 4 Mar 2026 14:31:19 +0100 Subject: [PATCH 1/7] Add Claude Code skills for content development workflows Introduces 10 skill definitions covering building, testing, rule creation, control file management, and PR workflows. Includes shared MCP fallback utilities for graceful degradation when the content-mcp server is unavailable. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/build-product/SKILL.md | 172 +++++++ .claude/skills/create-rule/SKILL.md | 585 ++++++++++++++++++++++++ .claude/skills/draft-pr/SKILL.md | 181 ++++++++ .claude/skills/inspect-control/SKILL.md | 93 ++++ .claude/skills/map-controls/SKILL.md | 261 +++++++++++ .claude/skills/map-requirement/SKILL.md | 231 ++++++++++ .claude/skills/onboard-control/SKILL.md | 313 +++++++++++++ .claude/skills/run-tests/SKILL.md | 151 ++++++ .claude/skills/shared/mcp_fallbacks.md | 217 +++++++++ .claude/skills/test-rule/SKILL.md | 352 ++++++++++++++ 10 files changed, 2556 insertions(+) create mode 100644 .claude/skills/build-product/SKILL.md create mode 100644 .claude/skills/create-rule/SKILL.md create mode 100644 .claude/skills/draft-pr/SKILL.md create mode 100644 .claude/skills/inspect-control/SKILL.md create mode 100644 .claude/skills/map-controls/SKILL.md create mode 100644 .claude/skills/map-requirement/SKILL.md create mode 100644 .claude/skills/onboard-control/SKILL.md create mode 100644 .claude/skills/run-tests/SKILL.md create mode 100644 .claude/skills/shared/mcp_fallbacks.md create mode 100644 .claude/skills/test-rule/SKILL.md diff --git a/.claude/skills/build-product/SKILL.md b/.claude/skills/build-product/SKILL.md new file mode 100644 index 000000000000..fbbc053660b5 --- /dev/null +++ b/.claude/skills/build-product/SKILL.md @@ -0,0 +1,172 @@ +--- +name: build-product +description: Build a ComplianceAsCode product +--- + +# Build Product + +Build a ComplianceAsCode product. + +**Product**: $ARGUMENTS + +## Tool Strategy + +This skill uses `mcp__content-mcp__*` tools when available (preferred — deterministic, structured results). When the MCP server is not configured, fall back to filesystem-based alternatives noted as **Fallback** in each step. See `.claude/skills/shared/mcp_fallbacks.md` for detailed fallback procedures. The skill must complete successfully either way. + +## Phase 1: Validate Product + +1. **Check if product is valid**: + Use `mcp__content-mcp__get_product_details` with `product_id=$ARGUMENTS` to validate the product exists and get its metadata. + **Fallback**: Read `products/$ARGUMENTS/product.yml` directly. If the file doesn't exist, the product is invalid. + +2. **If product not found**, list available products: + Use `mcp__content-mcp__list_products` to get all available products. + **Fallback**: Run `ls products/` to list available product directories. + +3. **If no product specified**, ask user using AskUserQuestion: + - Use the product list to populate options + - Allow "Other" for unlisted products + +## Phase 2: Build Product + +Build all artifacts (SCAP, guides, playbooks, tables): +```bash +./build_product $ARGUMENTS +``` + +### Build Output + +Monitor build progress: +- CMake configuration +- Content resolution +- OVAL generation +- XCCDF generation +- Data stream assembly + +Expected artifacts in `build/`: +- `ssg-$ARGUMENTS-ds.xml` - SCAP data stream +- `ssg-$ARGUMENTS-ds-1.2.xml` - SCAP 1.2 data stream +- `ssg-$ARGUMENTS-xccdf.xml` - XCCDF document +- `ssg-$ARGUMENTS-oval.xml` - OVAL definitions +- `guides/` - HTML guides +- `ansible/` - Ansible playbooks +- `bash/` - Bash scripts + +## Phase 3: Verify Build Success + +1. **Check build exit code**: + - Exit 0 = Success + - Non-zero = Build failed + +2. **Verify key artifacts exist**: + Use `mcp__content-mcp__get_datastream_info` with `product=$ARGUMENTS` to verify the datastream was built successfully and get artifact details. + **Fallback**: Check files directly: + ```bash + ls -la build/ssg-$ARGUMENTS-ds.xml + ls -la build/ssg-$ARGUMENTS-xccdf.xml + ls -la build/ssg-$ARGUMENTS-oval.xml + ``` + +3. **Check for build warnings**: + - Look for deprecation warnings + - Template processing warnings + - Missing reference warnings + +## Phase 4: Report Results + +### Success Report + +``` +Build Complete +============== + +Product: $ARGUMENTS + +Build Status: SUCCESS + Artifacts: + - build/ssg-$ARGUMENTS-ds.xml + - build/ssg-$ARGUMENTS-xccdf.xml + - build/ssg-$ARGUMENTS-oval.xml + +Ready for: + - Validation tests: /run-tests + - Automatus testing: /test-rule + - OpenSCAP scanning: oscap xccdf eval --profile build/ssg-$ARGUMENTS-ds.xml + - PR creation +``` + +### Build Failure Report + +``` +Build Failed +============ + +Product: $ARGUMENTS + +Error Output: +[error message from build] + +Common Causes: + 1. Jinja2 template syntax error in rule.yml + 2. Missing macro or variable reference + 3. Invalid platform specification + 4. Circular dependency in profiles + +Debugging Steps: + 1. Check the specific file mentioned in the error + 2. Validate YAML: python3 -c "import yaml; yaml.safe_load(open('path/to/file.yml'))" + 3. Check Jinja2: Look for unclosed tags, missing macros + 4. Review recent changes: git diff HEAD~1 +``` + +## Troubleshooting + +### Common Build Errors + +1. **CMake configuration failed**: + ```bash + # Ensure CMake is installed + cmake --version + + # Try with explicit generator + cd build && cmake -G "Unix Makefiles" .. + ``` + +2. **Python import errors**: + ```bash + # Install requirements + pip3 install -r requirements.txt + pip3 install -r test-requirements.txt + ``` + +3. **Missing dependencies**: + ```bash + # RHEL/Fedora + dnf install cmake make openscap-utils python3-pyyaml python3-jinja2 + ``` + +4. **Jinja2 errors**: + - Check for undefined macros + - Verify macro imports in the file + - Check for syntax errors in `{{{ }}}` blocks + +5. **OVAL validation errors**: + - Check template parameters match expected types + - Verify referenced variables exist + - Check platform applicability + +### Verbose Build + +For more detailed output: +```bash +./build_product $ARGUMENTS 2>&1 | tee build.log +``` + +### Ninja Build (Faster) + +If ninja is available: +```bash +cd build +cmake -G Ninja .. +ninja $ARGUMENTS +``` diff --git a/.claude/skills/create-rule/SKILL.md b/.claude/skills/create-rule/SKILL.md new file mode 100644 index 000000000000..3212f319cb04 --- /dev/null +++ b/.claude/skills/create-rule/SKILL.md @@ -0,0 +1,585 @@ +--- +name: create-rule +description: Create a new security rule with all required components +--- + +# Create Rule + +Create a new security rule for ComplianceAsCode. This skill handles both templated and non-templated rules. + +**Rule ID**: $ARGUMENTS + +## Tool Strategy + +This skill uses `mcp__content-mcp__*` tools when available (preferred — deterministic, structured results). When the MCP server is not configured, fall back to filesystem-based alternatives noted as **Fallback** in each step. See `.claude/skills/shared/mcp_fallbacks.md` for detailed fallback procedures. The skill must complete successfully either way. + +## Phase 1: Validate Input + +1. **Validate rule ID format**: + - Must be lowercase with underscores (no hyphens or uppercase) + - Example valid IDs: `sshd_max_auth_tries`, `accounts_password_minlen` + +2. **Check if rule already exists**: + Use `mcp__content-mcp__search_rules` with `query=$ARGUMENTS` to check if a rule with this ID already exists. + **Fallback**: Use `Glob` to search for `**/$ARGUMENTS/rule.yml`. + - If rule exists, inform user and ask if they want to modify it instead + +3. **Determine rule location**: + - Ask user where to create the rule within the guide hierarchy + - Provide suggestions based on rule ID prefix (e.g., `sshd_*` goes under `linux_os/guide/services/ssh/ssh_server/`) + +## Phase 2: Determine Rule Type + +Use AskUserQuestion to ask the user: + +**Question**: "What type of rule do you want to create?" + +**Options**: +1. **Templated rule (Recommended)** - Uses an existing template for checks and remediations. Faster to create, inherits tests from template. +2. **Non-templated rule** - Custom OVAL, Bash, and Ansible implementations. Requires writing test scenarios. + +## Phase 3: Template Selection (if templated) + +If user chose templated rule: + +1. **List available templates** using `mcp__content-mcp__list_templates` to get all available templates with their descriptions. + **Fallback**: Run `ls shared/templates/` to list available template directories. + +2. **Common templates for quick reference**: + | Template | Use Case | Key Parameters | + |----------|----------|----------------| + | `sshd_lineinfile` | SSH server configuration | `parameter`, `value` | + | `package_installed` | Package installation | `pkgname` | + | `package_removed` | Package removal | `pkgname` | + | `service_enabled` | Service enablement | `servicename` | + | `service_disabled` | Service disablement | `servicename` | + | `file_permissions` | File permission checks | `filepath`, `filemode` | + | `file_owner` | File ownership | `filepath`, `uid_or_name` | + | `file_groupowner` | File group ownership | `filepath`, `gid_or_name` | + | `sysctl` | Sysctl settings | `sysctlvar`, `sysctlval` | + | `grub2_bootloader_argument` | Kernel boot args | `arg_name`, `arg_value` | + | `kernel_module_disabled` | Disable kernel modules | `kernmodule` | + | `mount_option` | Mount options | `mountpoint`, `mountoption` | + | `shell_lineinfile` | Shell variable settings | `path`, `parameter`, `value` | + | `auditd_lineinfile` | Auditd configuration | `parameter`, `value` | + | `lineinfile` | Generic line in file | `path`, `text` | + +3. **Ask for template selection** using AskUserQuestion + +4. **Get template parameter schema** using `mcp__content-mcp__get_template_schema` with `template_name=` to get the full parameter schema, supported languages, and documentation. + **Fallback**: Read `shared/templates//template.yml` or `template.py` for parameter definitions. Check existing rules using this template for usage examples. + +5. **Collect template variables**: + - Use the schema from step 4 to identify required and optional parameters + - Ask user for each required variable + +## Phase 4: Gather Rule Metadata + +Collect the following information using AskUserQuestion or prompts: + +### Required Fields + +1. **Title**: Short descriptive title (displayed in scan results) + - Example: "Disable SSH Root Login" + +2. **Description**: Detailed description using Jinja2 templating + - Can use macros like `{{{ sshd_config_file() }}}`, `{{{ describe_mount(...) }}}` + +3. **Rationale**: Why this rule is important for security + +4. **Severity**: One of `low`, `medium`, `high`, `unknown` + +### Identifiers + +5. **CCE identifiers**: Ask which RHEL products need CCEs (rhel8, rhel9, rhel10) + - Format: `cce@rhel9: CCE-XXXXX-X` + - **Automatic CCE allocation**: + 1. Read available CCEs from `shared/references/cce-redhat-avail.txt` + 2. For each requested product, take the first available CCE from the file + 3. After adding the CCE to `rule.yml`, remove it from `cce-redhat-avail.txt` to prevent reuse + - If user doesn't want CCEs now, leave blank (can be added later) + + ```bash + # Read first available CCE + head -1 shared/references/cce-redhat-avail.txt + + # After using a CCE, remove it from the file + sed -i '1d' shared/references/cce-redhat-avail.txt + ``` + +### References (Optional but Recommended) + +6. **Security references** - ask if user has any of: + - `nist`: NIST SP 800-53 controls (e.g., `AC-6(2),CM-6(a)`) + - `srg`: DISA SRG IDs (e.g., `SRG-OS-000480-GPOS-00227`) + - `stigid` and `cis` references are filled in automatically by placing the rule into an appropriate control file + +### Additional Fields + +7. **Platform** (optional): Platform applicability + - Example: `platform: machine` (not for containers) + - Example: `platform: package[openssh-server]` + +8. **OCIL** (optional): OCIL check description + - Often uses macros like `{{{ complete_ocil_entry_sshd_option(...) }}}` + +## Phase 5: Generate Rule + +### For Templated Rules + +Use `mcp__content-mcp__generate_rule_from_template` with: +- `template_name`: The selected template name +- `parameters`: Template variables collected from the user +- `rule_id`: $ARGUMENTS +- `product`: The target product + +This generates the rule directory and rule.yml with the template configuration automatically. + +**Fallback**: Create the rule directory and `rule.yml` manually using `Write` tool. Include the `template:` key with `name:` and `vars:` based on the selected template. + +### For Non-Templated Rules + +Use `mcp__content-mcp__generate_rule_boilerplate` with: +- `rule_id`: $ARGUMENTS +- `title`: The rule title +- `description`: The rule description +- `severity`: The severity level +- `product`: The target product +- `rationale`: The rationale (optional) +- `location`: The guide path (optional, e.g., `linux_os/guide/services/ssh`) + +**Fallback**: Create the directory structure and all files manually using `Write` tool, following the skeleton templates below. + +This generates the rule directory structure with skeleton files: +``` +/$ARGUMENTS/ +├── rule.yml +├── oval/ +│ └── shared.xml +├── bash/ +│ └── shared.sh +├── ansible/ +│ └── shared.yml +└── tests/ + ├── correct.pass.sh + └── wrong.fail.sh +``` + +### Post-Generation Customization + +After MCP generates the boilerplate, customize the generated files: +- Add CCE identifiers, references, and platform applicability to rule.yml +- For non-templated rules, implement the OVAL, Bash, Ansible, and test content + +### Skeleton Files for Non-Templated Rules + +**oval/shared.xml**: +```xml + + + {{{ oval_metadata("{DESCRIPTION}", rule_title=rule_title) }}} + + + + + + +``` + +Note: `rule_id` and `rule_title` are automatically populated during build from the rule.yml. + +**bash/shared.sh**: +```bash +# platform = multi_platform_all +# reboot = false +# strategy = configure +# complexity = low +# disruption = low + +# Remediation script for $ARGUMENTS +# TODO: Implement remediation +``` + +**ansible/shared.yml**: +```yaml +# platform = multi_platform_all +# reboot = false +# strategy = configure +# complexity = low +# disruption = low + +- name: "{TITLE}" + # TODO: Implement remediation + ansible.builtin.debug: + msg: "Remediation not yet implemented" +``` + +**tests/correct.pass.sh**: +```bash +#!/bin/bash +# packages = {REQUIRED_PACKAGES} + +# Set up compliant state +# TODO: Configure system to be compliant +``` + +**tests/wrong.fail.sh**: +```bash +#!/bin/bash +# packages = {REQUIRED_PACKAGES} + +# Set up non-compliant state +# TODO: Configure system to be non-compliant +``` + +## Phase 6: Add to Component + +Every rule must belong to a component. Components are defined in `components/*.yml` and map rules to their associated packages and groups. + +### Step 1: Identify Appropriate Component + +Based on the rule's purpose and location, suggest likely components: + +```bash +# List all available components +ls components/*.yml | sed 's|components/||;s|\.yml||' | sort +``` + +**Common component mappings**: +| Rule Prefix | Likely Component | +|-------------|------------------| +| `sshd_*` | `openssh` | +| `audit_*`, `auditd_*` | `audit` | +| `sudo_*` | `sudo` | +| `firewalld_*` | `firewalld` | +| `selinux_*` | `selinux` | +| `package_*_installed/removed` | Component for that package | +| `service_*_enabled/disabled` | Component for that service | +| `sysctl_*` | `kernel` or specific subsystem | +| `mount_option_*` | `systemd` | +| `grub2_*` | `grub2` | +| `file_permissions_*`, `file_owner_*` | Component for the file's package | + +### Step 2: Verify Component Exists + +```bash +# Check if suggested component exists +cat components/.yml +``` + +### Step 3: Ask User to Confirm or Select Component + +Use AskUserQuestion with options: +1. Suggested component (if identified) +2. Browse/search for another component +3. Create a new component + +### Step 4a: Add Rule to Existing Component + +If user selects an existing component: + +1. **Read the component file**: + ```bash + cat components/.yml + ``` + +2. **Add the rule ID** to the `rules:` list in alphabetical order: + ```yaml + rules: + - existing_rule_1 + - existing_rule_2 + - $ARGUMENTS # Add new rule here + - existing_rule_3 + ``` + +3. **If using a template**, verify the template is listed in `templates:` section. If not, add it: + ```yaml + templates: + - existing_template + - {TEMPLATE_NAME} # Add if not present + ``` + +### Step 4b: Create New Component + +If no suitable component exists: + +1. **Ask for component details**: + - Component name (lowercase, hyphenated, e.g., `my-service`) + - Associated packages (list) + - Associated groups (from rule hierarchy) + +2. **Create the component file** `components/.yml`: + ```yaml + groups: + - + name: + packages: + - + rules: + - $ARGUMENTS + templates: + - {TEMPLATE_NAME} # If templated rule + ``` + +3. **Verify the new component**: + ```bash + python3 -c "import yaml; yaml.safe_load(open('components/.yml'))" + ``` + +### Component File Structure Reference + +```yaml +groups: # Rule groups this component covers (from guide hierarchy) +- service_name +- service_name_server +name: component-name # Matches filename without .yml +packages: # Packages associated with this component +- package-name +- package-name-server +rules: # Rule IDs belonging to this component (alphabetical) +- rule_id_1 +- rule_id_2 +templates: # Templates used by rules in this component +- template_name +``` + +## Phase 7: Add to Profile(s) or Control File(s) + +Most profiles in the project reference control files rather than listing rules directly. When adding a rule to a profile that uses a control file, add the rule to the control file instead. + +### Step 1: List Available Profiles + +Use `mcp__content-mcp__list_profiles` with `product=` to list all available profiles for each target product. + **Fallback**: Run `ls products//profiles/*.profile` to list profiles. + +### Step 2: Ask User Which Profile(s) + +Use AskUserQuestion to ask which profile(s) to add the rule to. Allow multiple selection. + +### Step 3: Detect Control File Reference + +For each selected profile, read it and check if it references a control file: + +```bash +grep -E "^\s+-\s+\w+:all" products//profiles/.profile +``` + +**Control file reference patterns**: +- `cis_rhel9:all` or `cis_rhel9:all:l2_server` → Control file: `products/rhel9/controls/cis_rhel9.yml` +- `stig_rhel9:all` → Control file: `products/rhel9/controls/stig_rhel9.yml` +- `hipaa:all` → Control file: `controls/hipaa.yml` (shared across products) + +**How to find the control file**: +1. Extract the control ID from the selection (e.g., `cis_rhel9` from `cis_rhel9:all:l2_server`) +2. Look in `products//controls/.yml` first +3. If not found, look in `controls/.yml` + +### Step 4a: Add to Control File (if profile uses control file) + +If the profile references a control file: + +1. **Read the control file** to understand its structure +2. **Ask user for control placement**: + - For CIS: Ask for the section ID (e.g., `5.2.15` for SSH settings) + - For STIG: Ask for the STIG ID (e.g., `RHEL-09-123456`) + - Show the control file structure to help user decide + +3. **Determine the level(s)** for the rule: + - CIS: `l1_server`, `l2_server`, `l1_workstation`, `l2_workstation` + - STIG: `high`, `medium`, `low` + +4. **Add a new control entry** to the control file: + + **For CIS control files**: + ```yaml + - id: 5.2.15 + title: Ensure SSH MaxAuthTries is set to 4 or less (Automated) + levels: + - l1_server + - l1_workstation + status: automated + rules: + - $ARGUMENTS + ``` + + **For STIG control files**: + ```yaml + - id: RHEL-09-123456 + levels: + - medium + title: RHEL 9 must limit the number of SSH authentication attempts. + rules: + - $ARGUMENTS + status: automated + ``` + +5. **Insert in the correct position** (controls are typically ordered by ID) + +### Step 4b: Add Directly to Profile (if no control file) + +If the profile lists rules directly (no control file reference): + +1. Read the profile file +2. Add the rule ID to the `selections:` list +3. Maintain alphabetical order if the profile uses it + +### Control File Locations + +**Product-specific** (preferred for product-specific benchmarks): +- `products/rhel8/controls/*.yml` +- `products/rhel9/controls/*.yml` +- `products/rhel10/controls/*.yml` + +**Shared** (for cross-product policies): +- `controls/*.yml` + +### Common Control Files by Profile + +| Profile | Control File | +|---------|--------------| +| `cis.profile` (rhel9) | `products/rhel9/controls/cis_rhel9.yml` | +| `stig.profile` (rhel9) | `products/rhel9/controls/stig_rhel9.yml` | +| `hipaa.profile` | `controls/hipaa.yml` | +| `pci-dss.profile` | `controls/pcidss_4.yml` | +| `ospp.profile` | `controls/ospp.yml` | +| `anssi_bp28_*.profile` | `controls/anssi.yml` | + +### Reference Security Policies + +When adding to CIS or STIG control files, reference the corresponding security policy document to find the correct control ID: +- CIS RHEL 9: `security_policies/CIS_Red_Hat_Enterprise_Linux_9_Benchmark_v2.0.0.md` +- STIG RHEL 9: `security_policies/Red Hat Enterprise Linux 9 STIG V2R5 - STIG-A-View.md` + +### Step 5: Update Profile Stability Test Data + +**IMPORTANT**: Whenever a rule is added to a profile (whether directly or via control file), the profile stability test data must also be updated. + +Profile stability test data is located in `tests/data/profile_stability//.profile` and contains a sorted list of rule IDs, one per line. + +1. **Check if stability test file exists**: + ```bash + ls tests/data/profile_stability//.profile + ``` + +2. **If the file exists**, add the rule ID in alphabetical order: + ```bash + # Read current file, add new rule, sort, and write back + (cat tests/data/profile_stability//.profile; echo "$ARGUMENTS") | sort -u > /tmp/profile_stability_tmp + mv /tmp/profile_stability_tmp tests/data/profile_stability//.profile + ``` + +3. **If the file doesn't exist**, this may be a new profile or a product without stability tests. Check if the product directory exists: + ```bash + ls tests/data/profile_stability// + ``` + - If the product directory exists but the profile file doesn't, create it with just the new rule + - If the product directory doesn't exist, skip this step (stability tests may not be set up for this product) + +**Profile stability test file format**: +``` +rule_id_1 +rule_id_2 +rule_id_3 +... +``` + +Rules are listed one per line, sorted alphabetically, with no duplicates. + +**Products with stability tests**: +- `rhel8` +- `rhel9` +- `rhel10` +- `fedora` + +## Phase 8: Verify and Report + +1. **Verify created files**: + ```bash + ls -la / + cat /rule.yml + ``` + +2. **Validate rule YAML** using `mcp__content-mcp__validate_rule_yaml` with the content of the generated rule.yml. This validates syntax, structure, and reference formats. + **Fallback**: Validate against the JSON schema: + ```bash + python3 -c " + import json, yaml, sys + from jsonschema import validate, ValidationError + schema = json.load(open('shared/schemas/rule.json')) + data = yaml.safe_load(open(sys.argv[1])) + try: + validate(instance=data, schema=schema) + print('Valid') + except ValidationError as e: + print(f'Invalid: {e.message}') + " + ``` + + For control files, use `mcp__content-mcp__validate_control_file` with the control file path. + **Fallback**: Validate against the control schema: + ```bash + python3 -c " + import json, yaml, sys + from jsonschema import validate, ValidationError + schema = json.load(open('shared/schemas/control.json')) + data = yaml.safe_load(open(sys.argv[1])) + try: + validate(instance=data, schema=schema) + print('Valid') + except ValidationError as e: + print(f'Invalid: {e.message}') + " + ``` + +3. **Verify CCE was removed** from available list (if CCE was allocated): + ```bash + grep "" shared/references/cce-redhat-avail.txt # should return nothing + ``` + +4. **Verify rule is in component**: + ```bash + grep "$ARGUMENTS" components/.yml + ``` + +5. **Verify profile stability test data** (if profile was modified): + ```bash + grep "$ARGUMENTS" tests/data/profile_stability//.profile + ``` + +7. **Report to user**: + - List all created files + - Show CCE allocations (and confirm removal from available list) + - Show component file modification + - Show control file or profile modifications + - Show profile stability test data updates + - Explain that `stigid` and `cis` references will be automatically populated from the control file + - Provide next steps: + - For templated rules: "Use `/test-rule $ARGUMENTS` to test" + - For non-templated rules: "Complete the OVAL, Bash, Ansible, and test files, then use `/test-rule $ARGUMENTS`" + - "Use `/build-product ` to build and `/run-tests` to validate" + +## Important Notes + +- **Do NOT make test files executable** - the test framework handles this +- **Use proper Jinja2 syntax** for macros in description, rationale, etc. +- **Check existing similar rules** for reference on structure and content +- **Templated rules are preferred** when a suitable template exists +- **Every rule must belong to a component** - add to existing component or create new one +- **Control files are preferred over direct profile modification** - they provide better structure and automatic reference population +- **CCE identifiers must be unique** - always remove allocated CCEs from `cce-redhat-avail.txt` +- **Update profile stability test data** - when adding to a profile, also update `tests/data/profile_stability//.profile` + +## Error Handling + +- If rule creation fails, clean up any partially created files +- If CCE allocation fails, do not remove CCEs from the available list +- Validate YAML syntax before writing +- Check for common mistakes: + - Missing required fields + - Invalid template names + - Malformed references + - Duplicate control IDs in control files + - Invalid level names in control files + - Rule not added to any component + - Component file syntax errors + - Profile stability test data not updated diff --git a/.claude/skills/draft-pr/SKILL.md b/.claude/skills/draft-pr/SKILL.md new file mode 100644 index 000000000000..1f442b2d8cb6 --- /dev/null +++ b/.claude/skills/draft-pr/SKILL.md @@ -0,0 +1,181 @@ +--- +name: draft-pr +description: Draft a PR description based on branch changes and the project PR template +--- + +# Draft Pull Request Description + +Generate a pull request description for the current branch by analyzing commits since the branching point from master, following the project's PR template format. + +## Tool Strategy + +This skill uses `mcp__content-mcp__*` tools when available (preferred — deterministic, structured results). When the MCP server is not configured, fall back to filesystem-based alternatives noted as **Fallback** in each step. See `.claude/skills/shared/mcp_fallbacks.md` for detailed fallback procedures. The skill must complete successfully either way. + +## Phase 1: Gather Branch Data + +1. **Get current branch and branching point**: + ```bash + git branch --show-current + git merge-base HEAD master + ``` + +2. **Abort if not on a feature branch**: + - If on `master`, or if `merge-base` equals `HEAD` (no diverging commits), inform the user: + "No diverging commits found. Please switch to your feature branch first." + - Stop execution. + +3. **Collect commit history** (all commits since branching point): + ```bash + MERGE_BASE=$(git merge-base HEAD master) + git log --no-merges --format="%H %s%n%b---" ${MERGE_BASE}..HEAD + ``` + +4. **Collect diff information**: + ```bash + MERGE_BASE=$(git merge-base HEAD master) + git diff --stat ${MERGE_BASE}..HEAD + git diff ${MERGE_BASE}..HEAD + git diff --name-only ${MERGE_BASE}..HEAD + ``` + +## Phase 2: Analyze Changes + +### 2.1 Categorize Change Type + +Determine which categories apply based on changed files and diff content: +- New rule (new `rule.yml` added) +- Modified rule (existing `rule.yml` changed) +- New or modified template (`shared/templates/`) +- Remediation changes (Bash/Ansible files) +- Profile changes (`.profile` files) +- Control file changes (`controls/`) +- Test scenario changes (`tests/`) +- Build system or infrastructure changes +- Documentation changes + +### 2.2 Identify Affected Products + +Look for product indicators: +- CCE identifiers in `rule.yml` files (`cce@rhel8`, `cce@rhel9`, `cce@rhel10`, etc.) +- Product-specific paths (`products//`) +- Profile or control file names containing product identifiers +- Platform applicability in rule definitions + +### 2.3 Read Key Changed Files + +For significant changed files, use MCP functions to get structured metadata: +- **Rules**: Use `mcp__content-mcp__get_rule_details` with the rule ID to get title, description, rationale, template, severity, references, and platform info +- **Profiles**: Use `mcp__content-mcp__get_profile_details` with product and profile ID to get profile structure and rule selections +- **Controls**: Use `mcp__content-mcp__get_control_details` to understand control framework structure +- **Templates**: Use `mcp__content-mcp__get_template_schema` to get template parameter info + +**Fallback**: Read the files directly — `rule.yml`, `.profile`, control YAML, and template files in `shared/templates//`. + +### 2.4 Check Test Coverage + +- Look for added/modified test scenarios in `tests/` subdirectories +- Check if rules use templates (inheriting template tests) + +### 2.5 Detect Issue References + +Scan commit messages for patterns like `Fixes #N`, `Resolves #N`, `Closes #N`, or bare `#N` references. + +## Phase 3: Draft PR Description + +Read the PR template: +```bash +cat .github/pull_request_template.md +``` + +Draft all three sections following the template format exactly: + +### 3.1 Description Section + +Auto-generate from analysis: + +- **New rules**: "Add new rule `` that . The rule has `` severity and targets . It uses the `