Skip to content

Latest commit

 

History

History
597 lines (464 loc) · 9.31 KB

File metadata and controls

597 lines (464 loc) · 9.31 KB

Minder Rule Type Quick Reference

Rule Type Template

---
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: {}

Ingestion Types

REST

ingest:
  type: rest
  rest:
    endpoint: "/repos/{{.Entity.Owner}}/{{.Entity.Name}}"
    parse: json
    fallback:
      - http_code: 404
        body: '{"error": "Not found"}'

Git

ingest:
  type: git
  git: {}

Diff

ingest:
  type: diff
  diff:
    ecosystems:
      - name: npm
        depfile: package-lock.json
      - name: go
        depfile: go.mod

Artifact

ingest:
  type: artifact
  artifact: {}

Evaluation Types

Rego: Deny-by-Default

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
      }

Rego: Constraints

eval:
  type: rego
  rego:
    type: constraints
    def: |
      package minder

      violations[{"msg": msg}] {
        # Logic to find violations
        msg := "Violation description"
      }

jq

eval:
  type: jq
  jq:
    - ingested:
        def: ".path.to.value"
      profile:
        def: ".expected_value"

Vulncheck

eval:
  type: vulncheck
  vulncheck: {}

Remediation Types

REST

remediate:
  type: rest
  rest:
    method: PATCH
    endpoint: "/repos/{{.Entity.Owner}}/{{.Entity.Name}}"
    body: |
      {"setting": "value"}

Pull Request

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 here

GitHub Branch Protection

remediate:
  type: gh_branch_protection
  gh_branch_protection:
    patch: |
      {"required_pull_request_reviews":{"required_approving_review_count":2}}

Template Variables

Available in Endpoints and Remediation

{{.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

Accessing Parameters

{{ $branch := index .Params "branch" }}
{{if ne $branch "" }}{{ $branch }}{{ else }}{{ .Entity.DefaultBranch }}{{ end }}

Rego Built-in Functions

File Operations

file.exists("path/to/file")           # Check existence
fileStr := file.read("path/to/file")  # Read content
files := file.ls("directory/path")    # List directory

String Operations

contains(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 whitespace

Parsing

yaml.unmarshal(yamlString)            # Parse YAML
json.unmarshal(jsonString)            # Parse JSON

Regex

regex.match(pattern, string)          # Match pattern

Collections

count(array)                          # Array length
some i; array[i]                      # Iterate array
some key; object[key]                 # Iterate object keys

Formatting

sprintf("Format %v %v", [arg1, arg2]) # Format string

Common Patterns

Check Boolean Setting

allow if {
  input.ingested.setting == true
}

Check Numeric Threshold

allow if {
  input.ingested.count >= input.profile.minimum
}

Check File Exists

allow if {
  file.exists(input.profile.filename)
}

Check File Contains Text

fileStr := file.read(input.profile.filename)

allow if {
  contains(fileStr, input.profile.required_text)
}

Parse YAML Configuration

allow if {
  fileStr := file.read(".github/config.yml")
  config := yaml.unmarshal(fileStr)
  config.setting == input.profile.expected_value
}

Check All Workflow Files

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 Based on Condition

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)
}

Multiple Allow Conditions

allow if {
  input.ingested.method_a == true
}

allow if {
  input.ingested.method_b == true
}

Complex Conditions

allow if {
  input.ingested.security.enabled == true
  input.ingested.security.level >= input.profile.min_level
  not input.ingested.archived
}

Schema Types

String

property_name:
  type: string
  description: "Description"
  default: "default_value"

Integer

property_name:
  type: integer
  description: "Description"
  default: 1

Boolean

property_name:
  type: boolean
  description: "Description"
  default: true

Enum

property_name:
  type: string
  description: "Description"
  enum:
    - option1
    - option2
    - option3
  default: option1

Array

property_name:
  type: array
  items:
    type: string
  description: "Description"

Object

property_name:
  type: object
  properties:
    sub_property:
      type: string
  description: "Description"

CLI Commands

Create Rule Type

minder ruletype create -f rule-types/github/my_rule.yaml

List Rule Types

minder ruletype list

Get Rule Type Details

minder ruletype get -n rule_name

Delete Rule Type

minder ruletype delete -n rule_name

Create Profile

minder profile create -f profile.yaml

Check Profile Status

minder profile status list --name profile-name --detailed

Testing

Test File Format

---
name: Test name
rule: rule_name
ingested:
  type: json
  value:
    # Mock ingested data
profile:
  # Mock profile configuration
expected:
  passed: true

Run Tests

go test ./...

Entity Types

Repository

def:
  in_entity: repository

Access:

  • {{.Entity.Owner}}
  • {{.Entity.Name}}
  • {{.Entity.DefaultBranch}}

Pull Request

def:
  in_entity: pull_request

Use with diff ingestion for dependency checks.

Artifact

def:
  in_entity: artifact

Use with artifact ingestion for signature verification.

Severity Levels

  • high: Critical security issues
  • medium: Important security practices
  • low: Best practices and hygiene

Release Phases

  • alpha: Experimental
  • beta: Stable API
  • ga: Production-ready

Alert Types

alert:
  type: security_advisory
  security_advisory: {}

Example: Complete Minimal Rule

---
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: {}

Debugging Tips

  1. Use trace() in Rego for debugging:

    trace(sprintf("Debug: %v", [variable]))
  2. Check profile status for detailed errors:

    minder profile status list --name profile-name --detailed
  3. Test ingestion separately:

    • Verify API endpoints manually
    • Check file paths in repository
    • Validate JSON/YAML parsing
  4. Validate schema:

    • Test with different profile configurations
    • Check required vs optional fields
    • Verify default values

Resources