with one click
onboard-control
// Onboard a new security policy as a control file. Parse the document, create control file structure, and map existing rules to requirements.
// Onboard a new security policy as a control file. Parse the document, create control file structure, and map existing rules to requirements.
| name | onboard-control |
| description | Onboard a new security policy as a control file. Parse the document, create control file structure, and map existing rules to requirements. |
Parse a security policy document (PDF, Markdown, HTML, or text), create a ComplianceAsCode control file structure from it, and optionally map existing rules to the extracted requirements.
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.
Without MCP server: PDF parsing is unavailable (use markdown/text instead). Control file generation and validation must be done manually. Cross-framework similarity search for auto-mapping is unavailable; keyword-based search is used instead.
Arguments: $ARGUMENTS — format: <document_path> [--id <policy_id>] [--product <product_id>] [--no-map]
Examples:
/onboard-control security_policies/anssi_r2.pdf --id anssi --product rhel9/onboard-control security_policies/cis_benchmark_v1.md --id cis_workstation --product fedora/onboard-control https://example.com/policy.html --id my_policy --no-map/onboard-control security_policies/stig.pdf --id rhel9_stig --product rhel9Parse arguments: Extract document_path, optional --id <policy_id>, optional --product <product_id>, and optional --no-map flag from $ARGUMENTS.
Infer document type from the file extension:
.pdf → pdf.md → markdown.html / .htm → htmltextValidate product (if specified): Call mcp__content-mcp__get_product_details with product_id.
mcp__content-mcp__list_products and present options via AskUserQuestion:
products/<product_id>/product.yml exists. List products with ls products/.If no --product specified, discover available products and ask the user via AskUserQuestion:
ls products/ (or call mcp__content-mcp__list_products) to get the list of available productsCheck for existing control: If --id was provided, call mcp__content-mcp__get_control_stats with control_id to check whether a control file with this ID already exists.
Fallback: Check if controls/<policy_id>.yml or products/<product>/controls/<policy_id>.yml exists.
AskUserQuestion:
Parse document: Call mcp__content-mcp__parse_policy_document with:
source: document_pathdocument_type: inferred type from Phase 1Display parse results:
## Parsed: {document_title}
- Sections found: {count}
- Requirements extracted: {count}
- Document type: {type}
Show extracted structure — present a summary of sections and requirements:
### Document Structure
| # | Section | Title | Requirements |
|---|---------|-------|-------------|
| 1 | 1.1 | Access Control | 5 |
| 2 | 1.2 | Authentication | 3 |
| ... | ... | ... | ... |
If no requirements were extracted (parsed document returned empty requirements):
AskUserQuestion:
id=section_id, title=section_title, description=section_contentAsk user to confirm via AskUserQuestion:
If --id not provided, suggest a policy ID based on the document title:
AskUserQuestion:
Ask for additional metadata via AskUserQuestion:
grep -h 'id:' controls/*.yml products/*/controls/*.yml | grep -A5 'levels:' | head -20 or call mcp__content-mcp__list_controls to see level schemes used in other frameworksAsk for version if not obvious from the document:
Ask for output format via AskUserQuestion:
controls_dir in parent file.controls list. Simpler for small policies with few requirements.Prepare requirements JSON: Format the extracted requirements as JSON for the generation tool:
{
"requirements": [
{
"id": "...",
"title": "...",
"description": "...",
"section": "..."
}
]
}
Generate control files: Call mcp__content-mcp__generate_control_files with:
policy_id: from Phase 3policy_title: from parsed document titleformat: "directory" or "inline" based on user choicerequirements_json: prepared JSONsource_document: original document pathversion: if providedlevels: if providedproduct: if specified (generates into products/<product>/controls/ instead of controls/)Write tool. Follow the shared/schemas/control.json schema for structure. For inline format, create a single YAML file with a controls: list. For directory format, create a parent YAML with controls_dir: and individual YAML files per requirement.Display generation result:
For directory format:
## Control Files Generated
- Parent file: {parent_file_path}
- Requirement files: {count} files in {directory}
- Errors: {count}
- Warnings: {count}
For inline format:
## Control File Generated
- File: {parent_file_path}
- Requirements: {count} controls in file
- Errors: {count}
- Warnings: {count}
If there are errors, display them and ask if the user wants to continue or abort.
Validate generated files: Call mcp__content-mcp__validate_control_file with the generated parent file path.
Fallback: Validate against the JSON 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>
Display validation results:
### Validation
- Valid: {yes/no}
- Errors: {list}
- Warnings: {list}
If validation fails, attempt to fix issues. Common fixes:
--no-map)If --no-map was NOT specified, offer to do an initial mapping pass.
Ask user via AskUserQuestion:
If "Yes, map all requirements": Hand off to the /map-controls workflow:
/map-controls {policy_id} --product {product} --policy {document_path}."If "Yes, quick auto-map only": Perform automatic mapping:
For each requirement:
a. Call mcp__content-mcp__find_similar_requirements with:
requirement_text: requirement title + " " + descriptionexclude_control_id: current policy_idmax_results: 5min_similarity: 0.4 (higher threshold for auto-mapping to avoid false positives)b. Collect all rules from similar requirements across frameworks.
c. If the target product is specified, call mcp__content-mcp__get_rule_product_availability for the top candidate rules to verify they're available for the product.
Fallback: Check each rule's rule.yml for cce@<product> identifiers.
d. If confident matches found (rules appear in 2+ other frameworks for the same concept):
mcp__content-mcp__update_requirement_rules with the matched rules and status="automated".
Fallback: Edit the requirement YAML directly using Edit tool.e. Track auto-mapped vs. skipped requirements.
After the auto-map pass, report results:
### Auto-Mapping Results
- Auto-mapped: {X} requirements
- Skipped (no confident match): {Y} requirements
- Total rules assigned: {Z}
| Requirement | Rules | Source Frameworks |
|-------------|-------|-------------------|
| {req_id} | {rules} | {frameworks} |
If "No": Skip mapping, proceed to Phase 6.
Present a summary of the onboarding:
## Control Onboarding Complete
**Policy**: {policy_title}
**Control ID**: {policy_id}
**Product**: {product or "global"}
**Source**: {document_path}
### Files Created
- Control file: {parent_file_path}
- Format: {inline or directory}
- Requirements: {count} (inline: in single file / directory: individual files in {directory})
### Mapping Status
Call mcp__content-mcp__get_control_stats with the new control_id (and product if specified) to show current state.
Fallback: Read the generated control YAML and count requirements by status.
| Status | Count |
|-----------|-------|
| Total | {total} |
| Mapped | {mapped} |
| Unmapped | {unmapped} |
| Coverage | {percentage}% |
### Next Steps
1. Review generated files: `git diff controls/` or `git status`
2. Inspect the control: `/inspect-control {policy_id}`
3. Map remaining requirements: `/map-controls {policy_id} --product {product} --policy {document_path}`
4. Map a single requirement: `/map-requirement {policy_id} <req_id> --product {product}`
5. Build product: `/build-product {product}`
6. Draft PR: `/draft-pr`
format parameter controls the structure:
"directory" (recommended for most policies): Creates a parent YAML file with a controls_dir reference and individual YAML files per requirement in a subdirectory. The subdirectory is flat (not nested by section)."inline": Creates a single monolithic YAML file with all requirements in a controls list. Suitable for small policies.--product is specified, files are generated in products/<product>/controls/ which takes precedence over global controls/ for that product./map-controls afterward for requirements that need manual attention.get_control_rule_index — it returns a 1.6MB+ payload. Use per-requirement find_similar_requirements instead.v3r4, "CIS Benchmark v1.0" → v1.0).Run Automatus tests for a security rule
Create a new security rule with all required components
Interactive control-to-rule mapping session. Walk through unmapped requirements, suggest rules using cross-framework analysis, and write selections to control files.
Map rules to a single control file requirement using cross-framework analysis and rule search.
Build a ComplianceAsCode product
Draft a PR description based on branch changes and the project PR template