136 lines
3.7 KiB
Rego
136 lines
3.7 KiB
Rego
# METADATA
|
|
# scope: package
|
|
# title: Post Edit Check - Builtin Policy
|
|
# authors: ["Cupcake Builtins"]
|
|
# custom:
|
|
# severity: MEDIUM
|
|
# id: BUILTIN-POST-EDIT
|
|
# routing:
|
|
# required_events: ["PostToolUse"]
|
|
# required_tools: ["Edit", "Write", "MultiEdit", "NotebookEdit"]
|
|
package cupcake.policies.builtins.post_edit_check
|
|
|
|
import rego.v1
|
|
|
|
# Run validation after file edits
|
|
ask contains decision if {
|
|
input.hook_event_name == "PostToolUse"
|
|
|
|
# Check if this was a file editing operation
|
|
editing_tools := {"Edit", "Write", "MultiEdit", "NotebookEdit"}
|
|
input.tool_name in editing_tools
|
|
|
|
# Get the file that was edited
|
|
file_path := get_edited_file_path
|
|
file_path != ""
|
|
|
|
# Get file extension
|
|
extension := get_file_extension(file_path)
|
|
extension != ""
|
|
|
|
# Run validation for this file type
|
|
validation_result := run_validation_for_extension(extension, file_path)
|
|
|
|
# If validation failed, ask for user confirmation
|
|
not validation_result.success
|
|
|
|
question := concat("\n", [
|
|
concat(" ", ["File validation failed for", file_path]),
|
|
validation_result.message,
|
|
"",
|
|
"Do you want to continue anyway?",
|
|
])
|
|
|
|
decision := {
|
|
"rule_id": "BUILTIN-POST-EDIT",
|
|
"reason": question,
|
|
"question": question,
|
|
"severity": "MEDIUM",
|
|
}
|
|
}
|
|
|
|
# Also provide feedback as context when validation succeeds
|
|
add_context contains context_msg if {
|
|
input.hook_event_name == "PostToolUse"
|
|
|
|
editing_tools := {"Edit", "Write", "MultiEdit", "NotebookEdit"}
|
|
input.tool_name in editing_tools
|
|
|
|
file_path := get_edited_file_path
|
|
file_path != ""
|
|
|
|
extension := get_file_extension(file_path)
|
|
extension != ""
|
|
|
|
validation_result := run_validation_for_extension(extension, file_path)
|
|
|
|
# If validation succeeded, provide positive feedback
|
|
validation_result.success
|
|
|
|
# add_context expects strings, not decision objects
|
|
context_msg := concat(" ", ["✓ Validation passed for", file_path])
|
|
}
|
|
|
|
# Extract file path from tool response/params
|
|
get_edited_file_path := path if {
|
|
path := input.params.file_path
|
|
} else := path if {
|
|
path := input.params.path
|
|
} else := ""
|
|
|
|
# Get file extension from path
|
|
get_file_extension(path) := ext if {
|
|
parts := split(path, ".")
|
|
count(parts) > 1
|
|
ext := parts[count(parts) - 1]
|
|
} else := ""
|
|
|
|
# Run validation for a specific file extension
|
|
run_validation_for_extension(ext, file_path) := result if {
|
|
# Check if there's a configured validation signal for this extension
|
|
signal_name := concat("", ["__builtin_post_edit_", ext])
|
|
signal_name in object.keys(input.signals)
|
|
|
|
# Get the validation result from the signal
|
|
signal_result := input.signals[signal_name]
|
|
|
|
# Parse the result based on its type
|
|
result := parse_validation_result(signal_result, file_path)
|
|
} else := result if {
|
|
# No validation configured for this extension
|
|
result := {
|
|
"success": true,
|
|
"message": "No validation configured - FALLBACK",
|
|
}
|
|
}
|
|
|
|
# Parse validation result from signal
|
|
parse_validation_result(signal_result, file_path) := result if {
|
|
# Handle object results with exit_code (standard format from signal execution)
|
|
is_object(signal_result)
|
|
"exit_code" in object.keys(signal_result)
|
|
|
|
result := {
|
|
"success": signal_result.exit_code == 0,
|
|
"message": default_validation_message(signal_result, file_path),
|
|
}
|
|
} else := result if {
|
|
# Handle string results (assume success if we got output)
|
|
is_string(signal_result)
|
|
result := {
|
|
"success": true,
|
|
"message": signal_result,
|
|
}
|
|
}
|
|
|
|
# Generate appropriate validation message
|
|
default_validation_message(signal_result, file_path) := msg if {
|
|
signal_result.output != ""
|
|
msg := signal_result.output
|
|
} else := msg if {
|
|
signal_result.exit_code == 0
|
|
msg := "Validation passed"
|
|
} else := msg if {
|
|
msg := concat("", ["Validation failed with exit code ", format_int(signal_result.exit_code, 10)])
|
|
}
|