Authoring custom packs
Pullminder rule packs are portable YAML files that define detection patterns or policy checks. You can create custom packs for your organization’s specific needs, test them locally, and publish them to the community registry or host them in a private registry.
Prerequisites
Section titled “Prerequisites”- Pullminder CLI installed (Installation guide)
- Familiarity with regular expressions
- A GitHub account (for publishing to the community registry)
Step 1: Scaffold a registry
Section titled “Step 1: Scaffold a registry”A registry is a directory that holds one or more rule packs. Start by scaffolding a new registry:
pullminder registry init my-rulesThis creates the following structure:
my-rules/ registry.yaml # Registry metadata packs/ # Pack definitions go here README.md # Registry documentationThe registry.yaml file identifies the registry and contains metadata used when syncing packs:
name: my-rulesdescription: Custom rule packs for my organizationauthor: your-github-handleurl: https://github.com/your-org/my-rulesStep 2: Add a pack
Section titled “Step 2: Add a pack”Use the CLI to scaffold a new pack inside the registry:
pullminder registry pack add \ --slug my-check \ --kind detection \ --name "My Check"This creates packs/my-check/pack.yaml with a minimal template.
Step 3: Edit pack.yaml
Section titled “Step 3: Edit pack.yaml”Open packs/my-check/pack.yaml and configure every field. Here is the full anatomy of a pack file:
slug: my-checkname: My Checkkind: detectionaction: flagversion: 1max_weight: 10
scoring: - min_findings: 1 score: 5 - min_findings: 3 score: 10
patterns: - name: Hardcoded database password rule_id: MC-001 regex: "(?i)db_password\\s*=\\s*[\"'][^\"']+[\"']" language: "*" severity: error category: security description: Database password is hardcoded in source code.
- name: Console log in production code rule_id: MC-002 regex: "console\\.log\\(" language: javascript severity: low category: code-quality description: Console.log statements should be removed before merging.
overrides: ignore_paths: - "**/testdata/**" - "**/fixtures/**" ignore_authors: - "dependabot[bot]"Field reference
Section titled “Field reference”| Field | Required | Description |
|---|---|---|
slug | Yes | Unique identifier for the pack. Lowercase, hyphens only. |
name | Yes | Human-readable display name. |
kind | Yes | Either detection (pattern matching) or policy (workflow rules). |
action | Yes | Default action when findings are produced: flag, warn, or block. |
version | Yes | Integer version of the pack (e.g., 3). Increment each time you modify patterns or configuration. |
schema_version | No | Schema version. Omit if not needed. The registry infers schema version from the pack structure. |
author | No | GitHub handle of the pack author. Required when publishing to the community registry. |
max_weight | No | Maximum weight a single finding from this pack can contribute to the risk score. Defaults to 10. |
scoring | No | Tiered scoring configuration. Each tier defines the minimum number of findings required to reach that score. The pack’s contribution to the risk score is the highest tier whose min_findings threshold is met. See Pack schema reference for details. |
patterns | Yes | Array of pattern objects. At least one pattern is required for detection packs. |
overrides | No | Path and author exclusions. |
Step 4: Write patterns
Section titled “Step 4: Write patterns”Each pattern in the patterns array defines a single detection rule. Patterns are matched against the added and modified lines in the PR diff.
Regex syntax
Section titled “Regex syntax”Patterns use Go-compatible regular expressions (RE2 syntax). A few tips:
- Use
(?i)at the start for case-insensitive matching. - Escape special characters:
\\.,\\(,\\{. - Use
[^\"']+to match non-empty strings inside quotes. - Use
\\bfor word boundaries to avoid false positives.
Language targeting
Section titled “Language targeting”The language field filters which files the pattern runs against:
| Value | Matches |
|---|---|
* | All files |
go | .go files |
python | .py files |
javascript | .js, .jsx, .mjs files |
typescript | .ts, .tsx files |
rust | .rs files |
ruby | .rb, .erb files |
php | .php files |
java | .java files |
yaml | .yaml, .yml files |
dockerfile | Dockerfile, *.dockerfile files |
terraform | .tf files |
Severity levels
Section titled “Severity levels”| Severity | Weight | Meaning |
|---|---|---|
critical | 10 | Immediate security risk. Typically warrants blocking the PR. |
error | 8 | Serious error that should be fixed before merging. |
high | 7 | Serious issue that should be addressed before merging. |
medium | 5 | Notable concern worth reviewing. |
low | 3 | Minor issue or style violation. |
info | 1 | Informational finding. Does not significantly affect the risk score. |
Step 5: Test the pack
Section titled “Step 5: Test the pack”Run the pack against a local repository or diff to verify it works:
pullminder rules test --pack my-check --verboseThis runs the pack against the current working directory’s latest diff and prints each matched pattern with the file, line number, and matched text.
To test against a specific diff:
pullminder rules test --pack my-check --diff ./path/to/diff.patchTo test against a specific file:
pullminder rules test --pack my-check --file ./src/config.pyStep 6: Validate the registry
Section titled “Step 6: Validate the registry”Before publishing, validate that all packs in the registry conform to the schema:
pullminder registry validate --strictThe --strict flag enables additional checks:
- Every pattern must have a unique
rule_id. - The
regexfield must compile without errors. - The
versionfield must be a positive integer. - The
severityfield must be one of the allowed values.
Fix any validation errors before proceeding.
Step 7: Publish to the community registry
Section titled “Step 7: Publish to the community registry”To share your pack with the Pullminder community:
pullminder rules publish --pack my-checkThis submits the pack for review. Once approved, it appears in the official registry and can be enabled by any Pullminder user from the dashboard or CLI.
Publishing requirements
Section titled “Publishing requirements”- The pack must pass
pullminder registry validate --strict. - The
authorfield must match your authenticated GitHub handle. - The pack must include at least one pattern with a description.
- The
slugmust not conflict with an existing pack in the registry.
Step 8: Use as a custom private registry
Section titled “Step 8: Use as a custom private registry”If you prefer to keep packs private, push the registry to a Git repository and configure it in the Pullminder dashboard:
- Push your registry to a private GitHub repository.
- In the Pullminder dashboard, navigate to Settings > Registries.
- Click Add registry and enter the repository URL.
- Pullminder syncs the registry and makes its packs available for your organization.
You can also configure a custom registry via the CLI:
pullminder registry add https://github.com/your-org/my-rules.gitPullminder pulls packs from custom registries on every sync cycle (default: every 15 minutes) and applies them alongside the official packs.