The Check Pipeline
Step-by-step breakdown of what happens when shellfirm intercepts a command
Every command you type passes through shellfirm's analysis pipeline before execution. Here is exactly what happens at each stage.
Pipeline stages
1. Shell hook intercepts the command
Your shell's pre-execution hook sends the command string to shellfirm pre-command. This happens before the shell executes anything -- the command is just a string at this point.
2. Quoted strings are stripped
shellfirm removes content inside single and double quotes to prevent evasion. Without this step, a command like rm -rf "/" could bypass pattern matching because the / is inside quotes.
Input: rm -rf "/important/data"
After: rm -rf
3. Command is split on shell operators
The command is split on &&, ||, |, ;, and newlines. Each part is analyzed independently:
Input: echo hello && rm -rf / || echo "safe"
Parts: ["echo hello", "rm -rf /", "echo \"safe\""]
This ensures that dangerous commands chained with safe ones are still caught.
4. Parallel pattern matching
Each command part is matched against all active check patterns using regex. Matching runs in parallel via rayon for fast throughput even with 100+ patterns.
Each check pattern has:
- A regex to match against the command
- A severity level (Info, Low, Medium, High, Critical)
- A group (fs, git, docker, kubernetes, etc.)
- Optional filters and alternatives
5. Post-match filters are applied
After a regex matches, post-match filters determine whether the check should actually fire. All filters in a check must pass (logical AND):
-
NotContains -- Keep the match only if the command does NOT contain a substring. Example:
git push --forcematches, butgit push --force-with-leasedoes not (filtered out byNotContains: "--force-with-lease"). -
Contains -- Keep the match only if the command DOES contain a substring.
-
PathExists -- Keep the match only if the captured file path actually exists on disk. This prevents false positives when you type
rm -rf some-typoand the path does not exist.
6. Runtime context is detected
shellfirm checks your environment for risk signals:
| Signal | How detected | Risk level |
|---|---|---|
| SSH session | SSH_CONNECTION or SSH_TTY environment variable | Elevated |
| Root user | EUID=0 | Critical |
| Protected git branch | git rev-parse --abbrev-ref HEAD matches main, master, production, or release/* | Critical |
| Production k8s context | kubectl config current-context contains prod, production, prd, or live | Critical |
| Production env vars | NODE_ENV=production, RAILS_ENV=production, ENVIRONMENT=production | Critical |
The detected risk level is used to escalate challenges.
7. Project policy is merged
If a .shellfirm.yaml file exists in the current directory or any parent directory, its rules are merged with your global settings. Policies are additive only -- they can escalate severity or add deny-listed patterns, but they can never weaken global protections.
See Team Policies for details.
8. Severity threshold is applied
If you have configured a min_severity threshold, checks below that threshold are skipped. Skipped checks are still logged to the audit trail with outcome Skipped, so you have full visibility.
9. Blast radius is computed
For supported check groups, shellfirm computes the real-world impact of the command at runtime. This runs with a 3-second timeout and graceful degradation -- if any computation fails, the challenge prompt is shown without the blast radius line.
Examples:
rm -rf ./src-- "Deletes ~347 files (12.4 MB) in ./src"git push --force-- "Force-pushes 5 commits to origin/main"docker system prune -a-- "Prunes up to 12 images, 3 containers, 2 volumes"
See Blast Radius for supported groups and details.
10. Challenge is presented or command is allowed
If any checks matched (after filtering and severity thresholds), the challenge type is determined by the escalation pipeline:
- Start with the configured default (e.g., Math)
- Escalate based on the highest matched severity (Critical→Yes, High→Enter by default)
- Apply any group or check-id overrides from settings
- Escalate based on runtime context (SSH→Enter, root/prod→Yes)
- Apply project policy overrides from
.shellfirm.yaml
Each layer can only make the challenge harder, never easier.
If no checks matched, the command proceeds without interruption.
Pipeline result
The pipeline produces a PipelineResult containing:
- active_matches -- All checks that matched after filtering
- context -- Detected runtime context with risk level and labels
- alternatives -- Safer command suggestions
- blast_radii -- Computed impact metrics for matched checks
- max_severity -- Highest severity among all matches
- is_denied -- Whether any matched pattern is on the deny list