---
version: v1
release_phase: alpha|beta|ga
type: rule-type
name: your_rule_name
display_name: Human Readable Name
short_failure_message: Brief error message
severity:
value: high|medium|low
context:
provider: github|gitlab # or {} for provider-agnostic
description: |
What this rule checks
guidance: |
How to fix violations
def:
in_entity: repository|pull_request|artifact
param_schema: {}
rule_schema: {}
ingest: {}
eval: {}
remediate: {} # Optional
alert:
type: security_advisory
security_advisory: {}ingest:
type: rest
rest:
endpoint: "/repos/{{.Entity.Owner}}/{{.Entity.Name}}"
parse: json
fallback:
- http_code: 404
body: '{"error": "Not found"}'ingest:
type: git
git: {}ingest:
type: diff
diff:
ecosystems:
- name: npm
depfile: package-lock.json
- name: go
depfile: go.modingest:
type: artifact
artifact: {}eval:
type: rego
rego:
type: deny-by-default
def: |
package minder
import future.keywords.if
default allow := false
default skip := false
default message := "Default failure message"
allow if {
# Condition for passing
}
skip if {
# Condition for skipping
}eval:
type: rego
rego:
type: constraints
def: |
package minder
violations[{"msg": msg}] {
# Logic to find violations
msg := "Violation description"
}eval:
type: jq
jq:
- ingested:
def: ".path.to.value"
profile:
def: ".expected_value"eval:
type: vulncheck
vulncheck: {}remediate:
type: rest
rest:
method: PATCH
endpoint: "/repos/{{.Entity.Owner}}/{{.Entity.Name}}"
body: |
{"setting": "value"}remediate:
type: pull_request
pull_request:
title: "Fix: {{.Profile.issue}}"
body: |
Automated fix by Minder
contents:
- path: path/to/file
action: replace # or append, prepend
content: |
File content hereremediate:
type: gh_branch_protection
gh_branch_protection:
patch: |
{"required_pull_request_reviews":{"required_approving_review_count":2}}{{.Entity.Owner}} # Repository owner
{{.Entity.Name}} # Repository name
{{.Entity.DefaultBranch}} # Default branch
{{.Entity.RepoId}} # Repository ID (GitLab)
{{.Profile.property}} # Profile configuration values
{{.Params.property}} # Parameter values{{ $branch := index .Params "branch" }}
{{if ne $branch "" }}{{ $branch }}{{ else }}{{ .Entity.DefaultBranch }}{{ end }}file.exists("path/to/file") # Check existence
fileStr := file.read("path/to/file") # Read content
files := file.ls("directory/path") # List directorycontains(haystack, needle) # String contains
startswith(string, prefix) # String starts with
endswith(string, suffix) # String ends with
lower(string) # Convert to lowercase
upper(string) # Convert to uppercase
trim_space(string) # Remove whitespaceyaml.unmarshal(yamlString) # Parse YAML
json.unmarshal(jsonString) # Parse JSONregex.match(pattern, string) # Match patterncount(array) # Array length
some i; array[i] # Iterate array
some key; object[key] # Iterate object keyssprintf("Format %v %v", [arg1, arg2]) # Format stringallow if {
input.ingested.setting == true
}allow if {
input.ingested.count >= input.profile.minimum
}allow if {
file.exists(input.profile.filename)
}fileStr := file.read(input.profile.filename)
allow if {
contains(fileStr, input.profile.required_text)
}allow if {
fileStr := file.read(".github/config.yml")
config := yaml.unmarshal(fileStr)
config.setting == input.profile.expected_value
}violations[{"msg": msg}] {
workflows := file.ls("./.github/workflows")
some w
workflowstr := file.read(workflows[w])
workflow := yaml.unmarshal(workflowstr)
# Check workflow
some job_name
not workflow.jobs[job_name].permissions
msg := sprintf("Workflow '%v' missing permissions", [workflows[w]])
}skip if {
input.profile.skip_private_repos == true
input.ingested.private == true
}
skip if {
input.profile.apply_if_file != ""
not file.exists(input.profile.apply_if_file)
}allow if {
input.ingested.method_a == true
}
allow if {
input.ingested.method_b == true
}allow if {
input.ingested.security.enabled == true
input.ingested.security.level >= input.profile.min_level
not input.ingested.archived
}property_name:
type: string
description: "Description"
default: "default_value"property_name:
type: integer
description: "Description"
default: 1property_name:
type: boolean
description: "Description"
default: trueproperty_name:
type: string
description: "Description"
enum:
- option1
- option2
- option3
default: option1property_name:
type: array
items:
type: string
description: "Description"property_name:
type: object
properties:
sub_property:
type: string
description: "Description"minder ruletype create -f rule-types/github/my_rule.yamlminder ruletype listminder ruletype get -n rule_nameminder ruletype delete -n rule_nameminder profile create -f profile.yamlminder profile status list --name profile-name --detailed---
name: Test name
rule: rule_name
ingested:
type: json
value:
# Mock ingested data
profile:
# Mock profile configuration
expected:
passed: truego test ./...def:
in_entity: repositoryAccess:
{{.Entity.Owner}}{{.Entity.Name}}{{.Entity.DefaultBranch}}
def:
in_entity: pull_requestUse with diff ingestion for dependency checks.
def:
in_entity: artifactUse with artifact ingestion for signature verification.
- high: Critical security issues
- medium: Important security practices
- low: Best practices and hygiene
- alpha: Experimental
- beta: Stable API
- ga: Production-ready
alert:
type: security_advisory
security_advisory: {}---
version: v1
release_phase: beta
type: rule-type
name: example_check
display_name: Example Security Check
short_failure_message: Check failed
severity:
value: medium
context:
provider: github
description: |
Example rule description
guidance: |
How to fix this issue
def:
in_entity: repository
rule_schema:
type: object
ingest:
type: rest
rest:
endpoint: "/repos/{{.Entity.Owner}}/{{.Entity.Name}}"
parse: json
eval:
type: rego
rego:
type: deny-by-default
def: |
package minder
import future.keywords.if
default allow := false
allow if {
input.ingested.has_issues == true
}
alert:
type: security_advisory
security_advisory: {}-
Use
trace()in Rego for debugging:trace(sprintf("Debug: %v", [variable]))
-
Check profile status for detailed errors:
minder profile status list --name profile-name --detailed
-
Test ingestion separately:
- Verify API endpoints manually
- Check file paths in repository
- Validate JSON/YAML parsing
-
Validate schema:
- Test with different profile configurations
- Check required vs optional fields
- Verify default values