How to Create Custom Security Packs Using YAML Files in Destructive Command Guard
You can extend Destructive Command Guard (DCG) by placing a YAML file containing regex patterns in a directory listed under packs.enabled, then reloading the tool—no recompilation required.
Destructive Command Guard (DCG) is a protective layer for shell commands that matches inputs against security packs. These packs are data-driven definitions loaded at runtime from YAML files, allowing you to create custom security packs using YAML files that enforce organization-specific policies without modifying the Rust source code.
How Pack Discovery Works
When DCG initializes, it reads the packs.enabled array from your configuration—typically located in ~/.config/dcg/config.toml or a project-level dcg.toml—as defined in src/config.rs. The scanner in src/packs/mod.rs recursively searches every path in that array for *.yaml files.
Each discovered file is validated against the JSON Schema in docs/pack.schema.yaml. If validation passes, DCG deserializes the YAML using serde_yaml::from_str into the internal Pack struct. The patterns are compiled once into static Regex objects via lazy_static! and registered in a global registry.
YAML Pack Structure
A custom security pack consists of three top-level sections. The regex field uses fancy-regex syntax, supporting look-ahead and look-behind assertions for precise matching.
id: <pack-identifier> # e.g., mycompany.cloud
description: <human readable> # optional
patterns: # list of rule objects
- name: <rule-id> # stable identifier used for allow-listing
type: safe | destructive # safe = whitelist, destructive = blacklist
severity: critical | high | medium | low
regex: "<fancy-regex>" # pattern matched against normalized command
reason: <explanation> # displayed in denial JSON
remediation: # optional
safe_alternative: "<cmd>" # suggested safe command
The Loading and Evaluation Pipeline
After deserialization in src/packs/mod.rs, the evaluator (src/evaluator.rs) iterates over all loaded packs on every incoming command. It tests the command string against each pattern in the order defined, stopping at the first match. A type: safe pattern results in immediate allowance, while type: destructive triggers a denial JSON response that includes the reason and any remediation steps formatted by src/output/mod.rs.
Step-by-Step: Creating Your First Custom Pack
Follow these concrete steps to add a new rule set to DCG:
-
Create a YAML file in a directory covered by
packs.enabled(default:./packs). According tosrc/packs/mod.rs, any file with the.yamlextension is automatically considered. -
Define a unique pack ID using reverse-DNS notation (e.g.,
myorg.database). This identifier must be unique across all loaded packs as specified indocs/pack.schema.yaml. -
Add pattern objects with a stable
name, explicitly settypetosafeordestructive, assign aseveritylevel, and write aregexusing fancy-regex syntax. Consultsrc/packs/core/mod.rsfor examples of complex patterns. -
Provide a human-readable
reasonexplaining the rule's purpose. This string populates thereasonfield in the denial JSON generated bysrc/evaluator.rs. -
Optionally specify remediation by adding
safe_alternativeunder theremediationkey. The text insrc/output/mod.rsrenders this suggestion to the user. -
Validate syntax by running
cargo test --test pack_schema. The test intests/pack_schema.rsloads every YAML file against the official schema, catching structural errors before runtime. -
Enable the pack by appending its relative or absolute path to the
packs.enabledarray in your TOML configuration. -
Reload DCG. The next invocation automatically picks up the new definitions without requiring a rebuild.
Practical Examples
Blocking Unencrypted S3 Uploads
Create ./packs/custom/s3_unencrypted.yaml to prevent aws s3 cp commands that lack server-side encryption flags:
id: mycompany.storage
description: "Prevent uploading files to S3 without server-side encryption"
patterns:
- name: s3:unencrypted-upload
type: destructive
severity: high
regex: >-
^\s*aws\s+s3\s+cp\s+.*\s+s3://[^ ]+(?<!--sse|--server-side-encryption)\s*$
reason: "Uploading to S3 without SSE can expose data"
remediation:
safe_alternative: "aws s3 cp <src> s3://<dest> --sse AES256"
The negative look-behind (?<!--sse|--server-side-encryption) ensures the pattern only matches commands missing the encryption flags. When matched, DCG returns a denial with the specified remediation.
Preventing Destructive Git Commands
Define a pack at ./packs/custom/git_reset.yaml to block git reset --hard:
id: mycompany.git
patterns:
- name: git:reset-hard
type: destructive
severity: critical
regex: "^\\s*git\\s+reset\\s+--hard\\b"
reason: "git reset --hard rewrites history and discards changes"
remediation:
safe_alternative: "git stash && git reset --hard HEAD"
Execution yields a JSON response:
{
"hookSpecificOutput": {
"permissionDecision": "deny",
"ruleId": "git:reset-hard",
"packId": "mycompany.git",
"severity": "critical",
"reason": "git reset --hard rewrites history and discards changes",
"remediation": {
"safeAlternative": "git stash && git reset --hard HEAD"
}
}
}
Whitelisting Safe Docker Operations
To explicitly allow docker compose up while other Docker commands remain subject to broader rules, create ./packs/custom/docker_compose.yaml:
id: mycompany.docker
patterns:
- name: docker:compose-up
type: safe
severity: low
regex: "^\\s*docker\\s+compose\\s+up\\b"
reason: "docker compose up is considered safe"
Because this pattern uses type: safe, matching commands pass through silently without further evaluation.
Key Source Files for Pack Mechanics
Understanding these files helps debug custom pack behavior:
src/packs/mod.rs– Handles directory scanning, YAML deserialization, and pack registration.src/config.rs– Parsesconfig.tomland extracts thepacks.enabledlist.docs/pack.schema.yaml– The JSON Schema validating every pack definition.src/evaluator.rs– Core matching engine that iterates over packs and patterns.src/output/mod.rs– Formats denial JSON and renders remediation text.tests/pack_schema.rs– Unit test suite validating all YAML files against the schema.examples/packs/example.yaml– Canonical reference pack shipped with the repository.
Summary
- Custom security packs are YAML files that extend DCG without recompilation.
- Place packs in directories listed under
packs.enabledin your TOML config. - Each pack requires a unique
id, a list ofpatternswithregex,type,severity, and areason. - Use fancy-regex syntax for complex pattern matching, including look-behinds.
- Validate changes using
cargo test --test pack_schemabefore deploying. - The evaluator processes patterns in order, stopping at the first match to determine allow or deny.
Frequently Asked Questions
Can I include multiple patterns in a single YAML file?
Yes. A single pack YAML can define an arbitrary number of pattern objects under the patterns list. The evaluator checks them sequentially in the order they appear in the file, so place high-priority rules first.
What regex engine does DCG use for pattern matching?
DCG uses the fancy-regex crate, which supports advanced features like look-ahead and look-behind assertions. This allows you to write precise rules that match dangerous commands only when specific unsafe conditions are met.
How do I disable a core pack while keeping my custom packs?
Remove the core pack's path from the packs.enabled array in your config.toml. DCG only loads packs explicitly listed in that configuration, so simply omitting the path prevents registration without deleting source files.
Where should I store custom packs for system-wide use?
Store them in ~/.config/dcg/packs/ (or equivalent on your OS) and reference that directory in the packs.enabled array of your user-level ~/.config/dcg/config.toml. For project-specific rules, place packs in a ./packs directory and enable it via project-level dcg.toml.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →