一键导入
一键导入
Create a template for checks and remediations
Create a new product in ComplianceAsCode project
Create Automatus test scenarios to test the given rule.
Search for existing rules that match a given requirement text. Identify rules that implement a specific control.
Create or update a versioned profile pair (versioned + unversioned extends pattern).
Build a ComplianceAsCode product
| name | create-rule |
| description | Create a new security rule with all required components |
Create a new security rule for ComplianceAsCode. This skill handles both templated and non-templated rules.
Rule ID: $ARGUMENTS
This skill uses mcp__content-agent__* 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.
Validate rule ID format:
sshd_max_auth_tries, accounts_password_minlenCheck if rule already exists:
Use mcp__content-agent__search_rules with query=$ARGUMENTS to check if a rule with this ID already exists.
Fallback: Use Glob to search for **/$ARGUMENTS/rule.yml.
Determine rule location:
sshd_* goes under linux_os/guide/services/ssh/ssh_server/)Use AskUserQuestion to ask the user:
Question: "What type of rule do you want to create?"
Options:
If user chose templated rule:
List available templates using mcp__content-agent__list_templates to get all available templates with their descriptions.
Fallback: Run ls shared/templates/ to list available template directories.
Present available templates from the list obtained in step 1 and help the user select the right one based on their use case.
Ask for template selection using AskUserQuestion
Get template parameter schema using mcp__content-agent__get_template_schema with template_name=<selected_template> to get the full parameter schema, supported languages, and documentation.
Fallback: Read shared/templates/<template_name>/template.yml or template.py for parameter definitions. Check existing rules using this template for usage examples.
Collect template variables:
Collect the following information using AskUserQuestion or prompts:
Title: Short descriptive title (displayed in scan results)
Description: Detailed description using Jinja2 templating
{{{ sshd_config_file() }}}, {{{ describe_mount(...) }}}Rationale: Why this rule is important for security
Severity: One of low, medium, high, unknown. Default value would be medium.
CCE identifiers: Ask which RHEL products need CCEs (rhel8, rhel9, rhel10)
cce@rhel9: CCE-XXXXX-Xshared/references/cce-redhat-avail.txtrule.yml, remove it from cce-redhat-avail.txt to prevent reuse# 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
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 filePlatform (optional): Platform applicability
platform: machine (not for containers)platform: package[openssh-server]OCIL (optional): OCIL check description
{{{ complete_ocil_entry_sshd_option(...) }}}Use mcp__content-agent__generate_rule_from_template with:
template_name: The selected template nameparameters: Template variables collected from the userrule_id: $ARGUMENTSproduct: The target productThis 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.
Use mcp__content-agent__generate_rule_boilerplate with:
rule_id: $ARGUMENTStitle: The rule titledescription: The rule descriptionseverity: The severity levelproduct: The target productrationale: 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:
<location>/$ARGUMENTS/
├── rule.yml
├── oval/
│ └── shared.xml
├── bash/
│ └── shared.sh
├── ansible/
│ └── shared.yml
└── tests/
├── correct.pass.sh
└── wrong.fail.sh
After MCP generates the boilerplate, customize the generated files:
oval/shared.xml:
<def-group>
<definition class="compliance" id="{{{ rule_id }}}" version="1">
{{{ oval_metadata("{DESCRIPTION}", rule_title=rule_title) }}}
<criteria>
<!-- Add OVAL criteria here -->
</criteria>
</definition>
<!-- Add OVAL tests, objects, and states here -->
</def-group>
Note: rule_id and rule_title are automatically populated during build from the rule.yml.
bash/shared.sh:
# platform = multi_platform_all
# reboot = false
# strategy = configure
# complexity = low
# disruption = low
# Remediation script for $ARGUMENTS
# TODO: Implement remediation
ansible/shared.yml:
# 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:
#!/bin/bash
# packages = {REQUIRED_PACKAGES}
# Set up compliant state
# TODO: Configure system to be compliant
tests/wrong.fail.sh:
#!/bin/bash
# packages = {REQUIRED_PACKAGES}
# Set up non-compliant state
# TODO: Configure system to be non-compliant
Every rule must belong to a component. Components are defined in components/*.yml and map rules to their associated packages and groups.
Based on the rule's purpose and location, suggest likely components:
# List all available components
ls components/*.yml | sed 's|components/||;s|\.yml||' | sort
Finding the right component: Search existing component files for rules with a similar prefix to identify the likely component:
# Find which component contains rules with a similar prefix
grep -l '<rule_prefix>' components/*.yml
# Check if suggested component exists
cat components/<component_name>.yml
Use AskUserQuestion with options:
If user selects an existing component:
Read the component file:
cat components/<component_name>.yml
Add the rule ID to the rules: list in alphabetical order:
rules:
- existing_rule_1
- existing_rule_2
- $ARGUMENTS # Add new rule here
- existing_rule_3
If using a template, verify the template is listed in templates: section. If not, add it:
templates:
- existing_template
- {TEMPLATE_NAME} # Add if not present
If no suitable component exists:
Ask for component details:
my-service)Create the component file components/<component_name>.yml:
groups:
- <group_from_rule_hierarchy>
name: <component_name>
packages:
- <package_name>
rules:
- $ARGUMENTS
templates:
- {TEMPLATE_NAME} # If templated rule
Verify the new component:
python3 -c "import yaml; yaml.safe_load(open('components/<component_name>.yml'))"
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
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.
Use mcp__content-agent__list_profiles with product=<product> to list all available profiles for each target product.
Fallback: Run ls products/<product>/profiles/*.profile to list profiles.
Use AskUserQuestion to ask which profile(s) to add the rule to. Allow multiple selection.
For each selected profile, read it and check if it references a control file:
grep -E "^\s+-\s+\w+:all" products/<product>/profiles/<profile>.profile
Control file reference patterns:
cis_rhel9:all or cis_rhel9:all:l2_server → Control file: products/rhel9/controls/cis_rhel9.ymlstig_rhel9:all → Control file: products/rhel9/controls/stig_rhel9.ymlhipaa:all → Control file: controls/hipaa.yml (shared across products)How to find the control file:
cis_rhel9 from cis_rhel9:all:l2_server)products/<product>/controls/<control_id>.yml firstcontrols/<control_id>.ymlIf the profile references a control file:
Read the control file to understand its structure
Ask user for control placement:
5.2.15 for SSH settings)RHEL-09-123456)Determine the level(s) for the rule:
l1_server, l2_server, l1_workstation, l2_workstationhigh, medium, lowAdd a new control entry to the control file:
For CIS control files:
- 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:
- id: RHEL-09-123456
levels:
- medium
title: RHEL 9 must limit the number of SSH authentication attempts.
rules:
- $ARGUMENTS
status: automated
Insert in the correct position (controls are typically ordered by ID)
If the profile lists rules directly (no control file reference):
selections: listProduct-specific (preferred for product-specific benchmarks):
products/rhel8/controls/*.ymlproducts/rhel9/controls/*.ymlproducts/rhel10/controls/*.ymlShared (for cross-product policies):
controls/*.yml| 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 |
When adding to CIS or STIG control files, reference the corresponding security policy document to find the correct control ID:
security_policies/CIS_Red_Hat_Enterprise_Linux_9_Benchmark_v2.0.0.mdsecurity_policies/Red Hat Enterprise Linux 9 STIG V2R5 - STIG-A-View.mdIMPORTANT: 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/<product>/<profile>.profile and contains a sorted list of rule IDs, one per line.
Check if stability test file exists:
ls tests/data/profile_stability/<product>/<profile>.profile
If the file exists, add the rule ID in alphabetical order:
# Read current file, add new rule, sort, and write back
(cat tests/data/profile_stability/<product>/<profile>.profile; echo "$ARGUMENTS") | sort -u > /tmp/profile_stability_tmp
mv /tmp/profile_stability_tmp tests/data/profile_stability/<product>/<profile>.profile
If the file doesn't exist, this may be a new profile or a product without stability tests. Check if the product directory exists:
ls tests/data/profile_stability/<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.
Discover products with stability tests:
ls tests/data/profile_stability/
Verify created files:
ls -la <rule_directory>/
cat <rule_directory>/rule.yml
Validate rule YAML using mcp__content-agent__validate_rule_yaml with the content of the generated rule.yml. This validates syntax, structure, and reference formats.
Fallback: Validate against the JSON schema:
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}')
" <path_to_rule.yml>
For control files, use mcp__content-agent__validate_control_file with the control file path.
Fallback: Validate against the control schema:
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}')
" <path_to_control.yml>
Verify CCE was removed from available list (if CCE was allocated):
grep "<allocated_cce>" shared/references/cce-redhat-avail.txt # should return nothing
Verify rule is in component:
grep "$ARGUMENTS" components/<component>.yml
Verify profile stability test data (if profile was modified):
grep "$ARGUMENTS" tests/data/profile_stability/<product>/<profile>.profile
Report to user:
stigid and cis references will be automatically populated from the control file/test-rule $ARGUMENTS to test"/test-rule $ARGUMENTS"/build-product <product> to build and /run-tests to validate"ssg/jinja.py) avoid conflicts with YAML/XML curly braces:
{{{ expr }}} (triple braces), NOT {{ expr }}{{% stmt %}}, NOT {% stmt %}{{# comment #}}, NOT {# comment #}{{{ full_name }}}, {{{ describe_service_enable(service="auditd") }}}, {{% if product in ["rhel9"] %}}, {{% set var="value" %}}cce-redhat-avail.txttests/data/profile_stability/<product>/<profile>.profile