Authoring Custom Packs¶
Create custom standards packs for organization-specific rules.
Pack structure¶
Each pack is a Python package inside src/guard/packs/:
src/guard/packs/
my_custom_pack/
__init__.py # Module docstring (1 line)
pack.yaml # Pack metadata + rules
__init__.py¶
pack.yaml¶
name: "My Custom Pack"
description: "One-sentence description of the pack's purpose"
rules:
- id: MYPACK-001
name: rule-name
type: regex
pattern: "..."
severity: high
file_glob: "**/*.py"
Rule format¶
Required fields¶
| Field | Type | Description |
|---|---|---|
id |
string | Unique ID (prefix with pack abbreviation) |
name |
string | Kebab-case name |
type |
string | regex, required_pattern, file_policy, ast, or documentation_obligation |
severity |
string | critical, high, medium, low, or info |
Optional fields¶
| Field | Type | Description |
|---|---|---|
description |
string | What the rule detects |
file_glob |
string | Glob pattern for files to check |
pattern |
string | Regex pattern (for regex/required_pattern) |
all_patterns |
list | All patterns must match (composite regex) |
check |
string | Check function name (for file_policy) |
ast_check |
string | AST check function (for ast) |
params |
dict | Configurable thresholds |
autofix |
string | remove_line, comment_out, or replace |
replacement |
string | Replacement text (with autofix: replace) |
standard |
string | Regulatory standard name |
clause |
string | Standard clause reference |
remediation_guidance |
string | Fix instructions shown in reports |
tags |
list | Categorization tags |
Rule type examples¶
Regex¶
- id: MYPACK-001
name: no-hardcoded-passwords
type: regex
pattern: "(?i)password\\s*=\\s*[\"'][^\"']{4,}"
severity: critical
file_glob: "**/*.py"
remediation_guidance: "Use environment variables or a secrets manager"
Composite regex¶
- id: MYPACK-002
name: sql-injection-risk
type: regex
severity: critical
file_glob: "**/*.py"
all_patterns:
- "execute\\("
- "request\\.(GET|POST|form)"
Required pattern¶
- id: MYPACK-003
name: require-license-header
type: required_pattern
pattern: "Licensed under"
severity: low
file_glob: "src/**/*.py"
File policy¶
- id: MYPACK-004
name: max-file-length
type: file_policy
check: max_lines
severity: medium
file_glob: "**/*.py"
params:
max_lines: 400
Available checks: starts_with_docstring, max_lines, must_contain_pattern, must_not_import, max_function_count.
AST¶
- id: MYPACK-005
name: strict-complexity
type: ast
ast_check: high_complexity
severity: high
file_glob: "src/**/*.py"
params:
max_complexity: 7
Available checks: no_mutable_defaults, no_star_imports, high_complexity, no_nested_functions, no_global_variables, max_function_length.
Documentation obligation¶
- id: MYPACK-DOC-001
name: incident-response-plan
type: documentation_obligation
severity: info
standard: "Internal Policy"
clause: "IRP-01"
remediation_guidance: "Create and maintain an incident response plan"
Registering a pack¶
Add an entry to _BUILTIN_PACKS in src/guard/packs/registry.py:
_BUILTIN_PACKS: dict[str, str] = {
"fda-iec-62304": "guard.packs.fda_iec_62304",
"owasp-top-10": "guard.packs.owasp_top_10",
"soc2": "guard.packs.soc2",
"my-custom-pack": "guard.packs.my_custom_pack",
}
Enabling¶
Or in .guard.yaml:
Testing¶
sentra list-packs # Verify pack loads
sentra list-rules # Verify rules appear
sentra scan # Check findings
ID naming conventions¶
| Pack | Format | Example |
|---|---|---|
| FDA IEC 62304 | IEC62304-{category}-{num} |
IEC62304-CODE-001 |
| OWASP Top 10 | OWASP-{category}-{num} |
OWASP-A01-001 |
| SOC2 | SOC2-{category}-{num} |
SOC2-CC6-001 |
Use a consistent prefix for your organization.
Severity guidelines¶
| Severity | When to use |
|---|---|
critical |
Security vulnerabilities, data exposure |
high |
Bugs likely to cause issues, unsafe patterns |
medium |
Code quality, maintainability risks |
low |
Style issues, minor improvements |
info |
Documentation obligations, informational |