-
Notifications
You must be signed in to change notification settings - Fork 14
feat: POC for an ampel create policy command #399
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
Draft
jpower432
wants to merge
1
commit into
complytime:main
Choose a base branch
from
jpower432:test-ampel-ai-skill
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| # Migration from gemara2ampel to init --tool | ||
|
|
||
| This document describes how to migrate from using the manual `gemara2ampel` script to using AI-assisted policy creation with the `init --tool` command. | ||
|
|
||
| ## Overview | ||
|
|
||
| Previously, creating AMPEL policies from Gemara artifacts required: | ||
| 1. Running the `gemara2ampel` script manually | ||
| 2. Converting Gemara artifacts to AMPEL format | ||
| 3. Manually editing and validating policies | ||
|
|
||
| With the new `init --tool` command, you can: | ||
| 1. Generate AI agent skill definitions for your preferred tool (OpenCode, Cursor, Claude Code) | ||
| 2. Use AI assistants to help create AMPEL policies interactively | ||
| 3. Validate policies against the AMPEL schema automatically | ||
|
|
||
| ## Migration Steps | ||
|
|
||
| ### Step 1: Install Skill Definitions | ||
|
|
||
| Choose your AI coding assistant and install the appropriate skill definition: | ||
|
|
||
| ```bash | ||
| # For OpenCode (installs to .cursorrules) | ||
| complyctl-provider-ampel init --tool opencode | ||
|
|
||
| # For Cursor (installs to .cursor/commands/ampel-create-policy.md and .cursor/skills/ampel-create-policy/SKILL.md) | ||
| complyctl-provider-ampel init --tool cursor | ||
|
|
||
| # For Claude Code (installs to .claude/ampel-policy-tool.json) | ||
| complyctl-provider-ampel init --tool claude-code | ||
| ``` | ||
|
|
||
| The `init` command automatically creates the necessary directories and installs the skill definitions in the correct locations. | ||
|
|
||
| ### Step 3: Use AI Assistant to Create Policies | ||
|
|
||
| Instead of running `gemara2ampel` manually, use your AI assistant: | ||
|
|
||
| - **OpenCode**: Ask it to create an AMPEL policy from a Gemara requirement | ||
| - **Cursor**: Use the `/ampel-create-policy` slash command | ||
| - **Claude Code**: Invoke the `create_ampel_policy` tool | ||
|
|
||
| The AI assistant will: | ||
| - Understand the AMPEL policy format | ||
| - Convert Gemara requirements to AMPEL policies | ||
| - Validate against the schema | ||
| - Provide guidance on policy structure | ||
|
|
||
| ### Step 4: Validate Policies | ||
|
|
||
| Policies created with AI assistance should follow the AMPEL schema. The skill definitions include the complete JSON Schema for validation. | ||
|
|
||
| ## Benefits | ||
|
|
||
| - **Interactive**: Get help from AI as you create policies | ||
| - **Validated**: Schema validation ensures correct structure | ||
| - **Documented**: Skill definitions include conversion guidelines | ||
| - **Consistent**: AI follows the same patterns every time | ||
|
|
||
| ## gemara2ampel Script Status | ||
|
|
||
| The `gemara2ampel` script remains available in the [complytime-demos](https://github.com/complytime/complytime-demos) repository for batch conversion workflows. For interactive policy creation, prefer using `init --tool` with your AI coding assistant. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,30 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| _ "embed" | ||
|
|
||
| "github.com/complytime/complyctl/cmd/ampel-plugin/server" | ||
| "github.com/complytime/complyctl/pkg/plugin" | ||
| ) | ||
|
|
||
| //go:embed templates/command.md | ||
| var commandTemplate string | ||
|
|
||
| //go:embed templates/skill.md | ||
| var skillTemplate string | ||
|
|
||
| func main() { | ||
| // Register init command handler | ||
| if plugin.RegisterInit(plugin.InitTemplates{ | ||
| CommandTemplate: commandTemplate, | ||
| SkillTemplate: skillTemplate, | ||
| CommandName: "ampel-create-policy", | ||
| SkillName: "ampel-create-policy", | ||
| }) { | ||
| return | ||
| } | ||
|
|
||
| // Default behavior: serve the plugin | ||
| ampelPlugin := server.New() | ||
| plugin.Serve(ampelPlugin) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| --- | ||
| name: /ampel-create-policy | ||
| id: ampel-create-policy | ||
| category: Policy | ||
| description: Create an AMPEL policy from a Gemara requirement | ||
| --- | ||
|
|
||
| Create an AMPEL policy from a Gemara requirement. | ||
|
|
||
| I'll help you convert a Gemara requirement to an AMPEL policy file with the correct structure, including policy metadata, control references, and verification tenets. | ||
|
|
||
| --- | ||
|
|
||
| **Input**: The user should provide: | ||
| - Gemara requirement ID (e.g., 'ac-3') | ||
| - Compliance framework and control references (e.g., NIST-800-53 AC-3) | ||
| - Description of what the policy should verify | ||
|
|
||
| **Steps** | ||
|
|
||
| 1. **Gather requirement information** | ||
| - Extract or ask for the Gemara requirement ID | ||
| - Identify compliance control references (framework, class, id) | ||
| - Understand what the policy should verify | ||
|
|
||
| 2. **Create AMPEL policy structure** | ||
| - Policy id: Convert requirement ID to kebab-case (e.g., 'ac-3' → 'ac-3-access-control-enforcement') | ||
| - meta.description: Clear description of what the policy verifies | ||
| - meta.controls: Array of control references with framework, class, and id | ||
|
|
||
| 3. **Define verification tenets** | ||
| - Create tenets array with verification checks | ||
| - Each tenet should have: | ||
| - id: Unique tenet identifier | ||
| - code: CEL expression that evaluates the attestation data | ||
| - predicates.types: Attestation predicate type URIs | ||
| - assessment.message: Success message | ||
| - error.message and error.guidance: Failure remediation | ||
|
|
||
| 4. **Validate policy structure** | ||
| - Ensure all required fields are present: id, meta, tenets | ||
| - Verify meta has description and controls | ||
| - Check each tenet has id, code, and predicates fields | ||
|
|
||
| 5. **Write policy file** | ||
| - Save as JSON file with kebab-case name matching policy id | ||
| - Place in appropriate AMPEL policy directory | ||
|
|
||
| **Output** | ||
|
|
||
| After creating the policy: | ||
| - Policy file location | ||
| - Summary of policy structure | ||
| - List of tenets created | ||
| - Next steps (e.g., test with ampel verify) | ||
|
|
||
| **AMPEL Policy Structure** | ||
|
|
||
| AMPEL policies are JSON files with the following structure: | ||
|
|
||
| ```json | ||
| { | ||
| "id": "BP-1.01", | ||
| "meta": { | ||
| "description": "Validate branch protection settings require pull requests", | ||
| "controls": [ | ||
| { "framework": "repo-branch-protection", "class": "source-code", "id": "BP-1" } | ||
| ] | ||
| }, | ||
| "tenets": [ | ||
| { | ||
| "id": "01", | ||
| "code": "has(predicates[0].data.values) ? predicates[0].data.values.exists(rule, rule.type == \"update\") : false", | ||
| "predicates": { | ||
| "types": ["http://github.com/carabiner-dev/snappy/specs/branch-rules.yaml"] | ||
| }, | ||
| "assessment": { | ||
| "message": "Direct pushes are disabled in default branch. PR required." | ||
| }, | ||
| "error": { | ||
| "message": "Direct pushes are enabled so PRs are not required.", | ||
| "guidance": "Create a branch ruleset protecting your default branch and enable \"Restrict updates\"" | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| **Input Data Structure (Snappy Attestations)** | ||
|
|
||
| AMPEL policies evaluate in-toto attestations produced by snappy. The attestation structure is: | ||
|
|
||
| ```json | ||
| { | ||
| "_type": "https://in-toto.io/Statement/v1", | ||
| "subject": [...], | ||
| "predicateType": "http://github.com/carabiner-dev/snappy/specs/branch-rules.yaml", | ||
| "predicate": { | ||
| "data": { | ||
| "values": [ | ||
| { | ||
| "type": "update", | ||
| "parameters": {} | ||
| }, | ||
| { | ||
| "type": "pull_request", | ||
| "parameters": { | ||
| "required_approving_review_count": 1, | ||
| "dismiss_stale_reviews_on_push": true, | ||
| "require_last_push_approval": true, | ||
| "require_code_owner_review": false | ||
| } | ||
| }, | ||
| { | ||
| "type": "non_fast_forward", | ||
| "parameters": {} | ||
| } | ||
| ] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| **How Predicates Work** | ||
|
|
||
| 1. Each tenet specifies `predicates.types` array with predicate type URIs (e.g., `http://github.com/carabiner-dev/snappy/specs/branch-rules.yaml`) | ||
| 2. AMPEL matches attestations by predicate type URI | ||
| 3. Matched predicates are available in the `predicates` array (indexed by match order) | ||
| 4. Access attestation data via `predicates[0].data.values` (array of rule objects) | ||
|
|
||
| **CEL Expression Patterns** | ||
|
|
||
| The `code` field uses CEL (Common Expression Language) to evaluate attestation data: | ||
|
|
||
| - **Check if data exists**: `has(predicates[0].data.values)` | ||
| - **Find rule by type**: `predicates[0].data.values.exists(rule, rule.type == "update")` | ||
| - **Check rule parameters**: `predicates[0].data.values.exists(rule, rule.type == "pull_request" && rule.parameters.required_approving_review_count >= 1)` | ||
| - **Multiple conditions**: `predicates[0].data.values.exists(rule, rule.type == "pull_request" && rule.parameters.dismiss_stale_reviews_on_push == true)` | ||
|
|
||
| **Common Rule Types (GitHub Branch Protection)** | ||
|
|
||
| - `"update"`: Restricts direct pushes (requires PR) | ||
| - `"pull_request"`: Pull request requirements | ||
| - `parameters.required_approving_review_count`: Minimum approvals (integer) | ||
| - `parameters.dismiss_stale_reviews_on_push`: Dismiss stale reviews (boolean) | ||
| - `parameters.require_last_push_approval`: Require last push approval (boolean) | ||
| - `parameters.require_code_owner_review`: Require code owner review (boolean) | ||
| - `"non_fast_forward"`: Blocks force pushes | ||
|
|
||
| **Example CEL Expressions** | ||
|
|
||
| ```cel | ||
| // Check if direct pushes are disabled | ||
| has(predicates[0].data.values) ? predicates[0].data.values.exists(rule, rule.type == "update") : false | ||
|
|
||
| // Check minimum approval count | ||
| has(predicates[0].data.values) ? predicates[0].data.values.exists(rule, rule.type == "pull_request" && rule.parameters.required_approving_review_count >= 1) : false | ||
|
|
||
| // Check multiple PR requirements | ||
| has(predicates[0].data.values) ? predicates[0].data.values.exists(rule, rule.type == "pull_request" && rule.parameters.dismiss_stale_reviews_on_push == true && rule.parameters.require_last_push_approval == true) : false | ||
|
|
||
| // Check force push blocking | ||
| has(predicates[0].data.values) ? predicates[0].data.values.exists(rule, rule.type == "non_fast_forward") : false | ||
| ``` | ||
|
|
||
| **Gemara to AMPEL Conversion Guidelines** | ||
|
|
||
| - Map Gemara requirement ID to AMPEL policy id (kebab-case) | ||
| - Convert Gemara control references to meta.controls array format | ||
| - Transform Gemara assessment requirements into AMPEL tenets | ||
| - Each tenet represents a specific verification check | ||
| - Use CEL expressions to evaluate snappy attestation data structure |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Step 2?