Skip to content

Commit ec882a7

Browse files
authored
Add new check categories, fix regex bugs, and expand test coverage (#176) (#176)
- Add 5 new check categories: github, npm/yarn/pnpm, flyio, vercel, netlify (30 new rules) - Add new rules to existing categories: git stash clear, terraform destroy/state rm, fs shred/truncate, docker run --privileged, base halt/poweroff/init, network nft/ip route flush, shell curl|python, database drop index, kubernetes delete --all/apply --force - Fix 40 regex bugs: 13 false positives (substring matches on --force-if-includes, delete-user-policy, --delete-after, dropdown, heroku \s*, etc.) and 27 false negatives (missing long-form flags, intervening flags, sudo before shell, backtick syntax, etc.) - Add comprehensive edge case tests across all check files (300+ new test cases)
1 parent 1244818 commit ec882a7

43 files changed

Lines changed: 1746 additions & 57 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

shellfirm/checks/aws.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
- type: NotContains
1515
value: "--dryrun"
1616
- from: aws
17-
test: aws\s+ec2\s+terminate-instances
17+
test: aws\s+(?:--\S+\s+\S+\s+)*ec2\s+terminate-instances
1818
description: "Terminating EC2 instances permanently destroys them and their local storage."
1919
id: aws:ec2_terminate
2020
severity: High
@@ -28,7 +28,7 @@
2828
alternative: "aws rds delete-db-instance --skip-final-snapshot=false --final-db-snapshot-identifier <name>"
2929
alternative_info: "Create a final snapshot before deletion so data can be recovered."
3030
- from: aws
31-
test: aws\s+iam\s+delete-(user|role|policy|group)
31+
test: aws\s+iam\s+delete-(user|role|policy|group)(\s|$)
3232
description: "Deleting IAM resources can break access for services and users."
3333
id: aws:iam_delete
3434
severity: High

shellfirm/checks/base.yaml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
id: base:execute_all_history_commands
1515
severity: Critical
1616
- from: base
17-
test: reboot(\s|$)
17+
test: (^|\s)reboot(\s|$)
1818
description: "You are going to reboot your machine."
1919
id: base:reboot_machine
2020
severity: High
2121
- from: base
22-
test: shutdown(\s|$)
22+
test: (^|\s)shutdown(\s|$)
2323
description: "You are going to shutdown your machine."
2424
id: base:shutdown_machine
2525
severity: High
@@ -46,10 +46,20 @@
4646
id: systemd:disable_service
4747
severity: Medium
4848
- from: base
49-
test: systemctl\s+stop\s+(docker|sshd|nginx|apache2|httpd|postgresql|mysql|redis)(\s|$)
49+
test: systemctl\s+stop\s+(docker|sshd|nginx|apache2|httpd|postgresql|mysql|redis)[\w.-]*(\s|$)
5050
description: "Stopping a critical system service can cause outages."
5151
id: systemd:stop_critical_service
5252
severity: High
53+
- from: base
54+
test: (halt|poweroff)(\s|$)
55+
description: "This command will immediately power off your machine."
56+
id: base:poweroff_machine
57+
severity: High
58+
- from: base
59+
test: init\s+(0|6)(\s|$)
60+
description: "This command will shutdown (init 0) or reboot (init 6) your machine."
61+
id: base:init_shutdown_reboot
62+
severity: High
5363
- from: base
5464
test: ssh-add\s+-D
5565
description: "Removes all SSH identities from the agent."

shellfirm/checks/database.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@
4141
id: database:drop_schema_role_user
4242
severity: High
4343
- from: database
44-
test: (?i)ALTER\s+TABLE\s+\w+\s+DROP\s+COLUMN
44+
test: (?i)ALTER\s+TABLE\s+[\w.]+\s+DROP\s+COLUMN
4545
description: "Dropping a column permanently removes data from all rows."
4646
id: database:alter_drop_column
4747
severity: High
48+
- from: database
49+
test: (?i)DROP\s+INDEX
50+
description: "Dropping an index on a production table can cause catastrophic performance degradation."
51+
id: database:drop_index
52+
severity: Medium

shellfirm/checks/docker.yaml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
alternative: "docker system prune"
77
alternative_info: "Without -a, only dangling images are removed (not all unused images)."
88
- from: docker
9-
test: docker\s+rm\s+(-f|--force)\s+\$\(docker\s+ps
9+
test: docker\s+rm\s+(-f|--force)\s+(?:\$\(|`)docker\s+ps
1010
description: "Force removing all running or stopped containers."
1111
id: docker:force_remove_all_containers
1212
severity: High
@@ -31,7 +31,7 @@
3131
id: docker:remove_network
3232
severity: Medium
3333
- from: docker
34-
test: docker[\s-]compose\s+down\s+.*(-v|--volumes)
34+
test: docker[\s-]compose\s+.*down\s+.*(-v|--volumes)
3535
description: "This will stop containers AND delete all associated volumes and data."
3636
id: docker:compose_down_volumes
3737
severity: High
@@ -59,3 +59,11 @@
5959
description: "Clears the entire Docker build cache."
6060
id: docker:buildx_prune_all
6161
severity: High
62+
- from: docker
63+
test: docker\s+run\s+(.+\s+)?--privileged(\s|$|=true)
64+
description: "Running a container with --privileged gives it full access to the host system."
65+
id: docker:run_privileged
66+
severity: High
67+
filters:
68+
- type: NotContains
69+
value: "--privileged=false"

shellfirm/checks/flyio.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
- from: flyio
2+
test: fly(?:ctl)?\s+apps\s+destroy(\s|$)
3+
description: "Destroying a Fly.io app permanently removes it and all its resources."
4+
id: flyio:apps_destroy
5+
severity: Critical
6+
- from: flyio
7+
test: fly(?:ctl)?\s+secrets\s+unset\s+
8+
description: "Removing secrets can break running applications that depend on them."
9+
id: flyio:secrets_unset
10+
severity: High
11+
- from: flyio
12+
test: fly(?:ctl)?\s+volumes?\s+destroy(\s|$)
13+
description: "Destroying a volume permanently deletes all data stored on it."
14+
id: flyio:volumes_destroy
15+
severity: High
16+
- from: flyio
17+
test: fly(?:ctl)?\s+postgres\s+destroy(\s|$)
18+
description: "Destroying a Fly Postgres cluster permanently deletes the database and all its data."
19+
id: flyio:postgres_destroy
20+
severity: Critical

shellfirm/checks/fs.yaml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
- from: fs
2-
test: 'rm\s{1,}(?:-[rRfvV]+|--(?:force|recursive|verbose|preserve-root|no-preserve-root|one-file-system))(?:\s+(?:-[rRfvV]+|--(?:force|recursive|verbose|preserve-root|no-preserve-root|one-file-system)))*(?:\s+\S+)*?\s+(\*|\.{1,}|/)(?:\s|$)'
2+
test: 'rm\s{1,}(?:-[rRfvV]+|--(?:force|recursive|verbose|preserve-root|no-preserve-root|one-file-system))(?:\s+(?:-[rRfvV]+|--(?:force|recursive|verbose|preserve-root|no-preserve-root|one-file-system)))*(?:\s+\S+)*?\s+(\*|\.{1,}|/|\.\/\*)(?:\s|$)'
33
description: "You are going to delete everything in the path."
44
id: fs:recursively_delete
55
severity: Critical
@@ -125,7 +125,7 @@
125125
- type: PathExists
126126
value: 1
127127
- from: fs
128-
test: rsync\s+.*--delete
128+
test: rsync\s+.*--delete(\s|$)
129129
description: "Syncs with deletion — removes files at destination not present in source."
130130
id: fs:rsync_delete
131131
severity: High
@@ -141,3 +141,15 @@
141141
description: "Recursive ownership change on root or wildcard can break system files and SSH keys."
142142
id: fs:recursively_chown
143143
severity: Critical
144+
- from: fs
145+
test: shred\s+
146+
description: "Shred overwrites file data at the block level, making recovery impossible unlike rm."
147+
id: fs:shred
148+
severity: High
149+
alternative: "rm <file>"
150+
alternative_info: "Regular rm only unlinks the file. Use shred only when you truly need irrecoverable deletion."
151+
- from: fs
152+
test: truncate\s+.*-s\s*0\s
153+
description: "This command will zero out a file, permanently erasing its contents."
154+
id: fs:truncate_zero
155+
severity: High

shellfirm/checks/git.yaml

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
alternative: "git stash"
1010
alternative_info: "Saves your changes to the stash so you can recover them later with 'git stash pop'."
1111
- from: git
12-
test: git\s{1,}rm\s{1,}(\*|\.)
12+
test: git\s{1,}rm\s{1,}(?:-\S+\s+)*(\*|\.)(\s|$)
1313
description: "This command going to delete all files."
1414
id: git:delete_all
1515
severity: High
1616
- from: git
17-
test: git\s{1,}clean\s{1,}(?:-[a-zA-Z]*f[a-zA-Z]*d[a-zA-Z]*|-[a-zA-Z]*d[a-zA-Z]*f[a-zA-Z]*|(?:-\S+\s+)*-f(?:\s+-\S+)*\s+-d|(?:-\S+\s+)*-d(?:\s+-\S+)*\s+-f)
17+
test: git\s{1,}clean\s{1,}(?:-[a-zA-Z]*f[a-zA-Z]*d[a-zA-Z]*|-[a-zA-Z]*d[a-zA-Z]*f[a-zA-Z]*|(?:-\S+\s+)*-f(?:\s+-\S+)*\s+-d|(?:-\S+\s+)*-d(?:\s+-\S+)*\s+-f|--force\s+(?:-\S+\s+)*-d|(?:-\S+\s+)*-d(?:\s+-\S+)*\s+--force)
1818
description: "This command will remove all untracked files and directories."
1919
id: git:clean_force
2020
severity: High
@@ -33,6 +33,8 @@
3333
filters:
3434
- type: NotContains
3535
value: "--force-with-lease"
36+
- type: NotContains
37+
value: "--force-if-includes"
3638
alternative: "git push --force-with-lease"
3739
alternative_info: "Checks that your local ref is up-to-date before force pushing, preventing accidental overwrites of others' work."
3840
- from: git
@@ -43,7 +45,7 @@
4345
alternative: "git branch -d <branch>"
4446
alternative_info: "Uses safe delete (-d) which refuses to delete a branch with unmerged changes."
4547
- from: git
46-
test: git\s{1,}checkout\s{1,}(-f|--force)
48+
test: git\s{1,}checkout\s{1,}.*(-f\b|--force)
4749
description: "This command will force checkout and discard local changes."
4850
id: git:force_checkout
4951
severity: High
@@ -64,7 +66,7 @@
6466
alternative: "git stash"
6567
alternative_info: "Saves your changes to the stash so you can recover them later with 'git stash pop'."
6668
- from: git
67-
test: git\s{1,}rebase\s{1,}-i
69+
test: git\s{1,}rebase\s{1,}(-i\b|--interactive)
6870
description: "This command will start an interactive rebase which can modify commit history."
6971
id: git:interactive_rebase
7072
severity: Medium
@@ -76,12 +78,12 @@
7678
alternative: "git-filter-repo"
7779
alternative_info: "A faster, safer, and officially recommended alternative to filter-branch."
7880
- from: git
79-
test: git\s{1,}gc\s{1,}--prune=now
81+
test: git\s{1,}gc\s{1,}.*--prune=now
8082
description: "This command will permanently delete unreachable objects."
8183
id: git:gc_prune
8284
severity: High
8385
- from: git
84-
test: git\s{1,}update-ref\s{1,}-d
86+
test: git\s{1,}update-ref\s{1,}(-d\b|--delete)
8587
description: "This command will delete a Git reference."
8688
id: git:delete_ref
8789
severity: High
@@ -125,13 +127,20 @@
125127
description: "This command will apply or remove stashed changes."
126128
id: git-strict:stash_pop_drop
127129
severity: Low
130+
- from: git
131+
test: git\s{1,}stash\s{1,}clear(\s|$)
132+
description: "This command will permanently delete all stash entries with no way to recover them."
133+
id: git:stash_clear
134+
severity: High
135+
alternative: "git stash list"
136+
alternative_info: "Review your stashes before clearing them. Use 'git stash drop stash@{N}' to remove individual entries."
128137
- from: git
129138
test: git\s{1,}submodule\s{1,}(update|deinit)
130139
description: "This command will update or deinitialize Git submodules."
131140
id: git-strict:submodule_update
132141
severity: Low
133142
- from: git
134-
test: git\s{1,}tag\s{1,}-a
143+
test: git\s{1,}tag\s{1,}(-a\b|--annotate)
135144
description: "This command will create an annotated tag."
136145
id: git-strict:create_tag
137146
severity: Low
@@ -141,12 +150,12 @@
141150
id: git:push_mirror
142151
severity: Critical
143152
- from: git
144-
test: git\s{1,}push\s{1,}\S+\s{1,}--delete\s|git\s{1,}push\s{1,}\S+\s{1,}:\S
153+
test: git\s{1,}push\s{1,}(\S+\s{1,}--delete\s|--delete\s{1,}\S+\s|\S+\s{1,}:\S)
145154
description: "This command will delete a remote branch."
146155
id: git:push_delete_branch
147156
severity: High
148157
- from: git
149-
test: git\s{1,}reflog\s{1,}expire\s{1,}--expire=now
158+
test: git\s{1,}reflog\s{1,}expire\s{1,}.*--expire=now
150159
description: "Expiring all reflog entries destroys the last recovery mechanism for lost commits."
151160
id: git:reflog_expire
152161
severity: Critical

shellfirm/checks/github.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
- from: github
2+
test: gh\s+repo\s+delete(\s|$)
3+
description: "Permanently deletes a repository and all its data."
4+
id: github:repo_delete
5+
severity: Critical
6+
- from: github
7+
test: gh\s+repo\s+archive(\s|$)
8+
description: "Archiving a repository makes it read-only and can break workflows."
9+
id: github:repo_archive
10+
severity: High
11+
- from: github
12+
test: gh\s+repo\s+rename(\s|$)
13+
description: "Renaming a repository breaks all existing URLs, clones, and CI/CD references."
14+
id: github:repo_rename
15+
severity: High
16+
- from: github
17+
test: gh\s+repo\s+edit\s+.*--visibility
18+
description: "Changing repository visibility can expose private code or break public access."
19+
id: github:repo_change_visibility
20+
severity: High
21+
- from: github
22+
test: gh\s+release\s+delete(\s|$)
23+
description: "Deleting a release removes it and its assets permanently."
24+
id: github:release_delete
25+
severity: High
26+
- from: github
27+
test: gh\s+secret\s+delete(\s|$)
28+
description: "Deleting a secret can break CI/CD pipelines that depend on it."
29+
id: github:secret_delete
30+
severity: High
31+
- from: github
32+
test: gh\s+variable\s+delete(\s|$)
33+
description: "Deleting a variable can break CI/CD pipelines that depend on it."
34+
id: github:variable_delete
35+
severity: High

shellfirm/checks/heroku.yaml

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,95 @@
11
- from: heroku
2-
test: heroku\s*ps:restart
2+
test: heroku\s+ps:restart
33
description: "Restarting app dynos"
44
id: heroku:restart_app_dynos
55
severity: Medium
66
- from: heroku
7-
test: heroku\s*ps:stop
7+
test: heroku\s+ps:stop
88
description: "Stop app dynos"
99
id: heroku:stop_app_dynos
1010
severity: High
1111
- from: heroku
12-
test: heroku\s*ps:kill
12+
test: heroku\s+ps:kill
1313
description: "Kill app dynos"
1414
id: heroku:kill_app_dynos
1515
severity: High
1616
- from: heroku
17-
test: heroku\s*maintenance:on
17+
test: heroku\s+maintenance:on
1818
description: "Put the app into maintenance mode?"
1919
id: heroku:enable_maintenance_mode
2020
severity: High
2121
- from: heroku
22-
test: heroku\s*members:remove
22+
test: heroku\s+members:remove
2323
description: "Removes a user from a team"
2424
id: heroku:remove_member
2525
severity: High
2626
- from: heroku
27-
test: heroku\s*features:disable
27+
test: heroku\s+features:disable
2828
description: "Disables an app feature"
2929
id: heroku:disable_app_feature
3030
severity: Medium
3131
- from: heroku
32-
test: heroku\s*container:rm
32+
test: heroku\s+container:rm
3333
description: "Remove the process type from your app"
3434
id: heroku:remove_app_container
3535
severity: High
3636
- from: heroku
37-
test: heroku\s*config:unset
37+
test: heroku\s+config:unset
3838
description: "unset one or more config vars"
3939
id: heroku:unset_environment_variable
4040
severity: Medium
4141
- from: heroku
42-
test: heroku\s*clients:destroy
42+
test: heroku\s+clients:destroy
4343
description: "Delete client by ID"
4444
id: heroku:destroy_client
4545
severity: High
4646
- from: heroku
47-
test: heroku\s*clients:rotate
47+
test: heroku\s+clients:rotate
4848
description: "Rotate OAuth client secret"
4949
id: heroku:rotate_oauth_client
5050
severity: Medium
5151
- from: heroku
52-
test: heroku\s*clients:update
52+
test: heroku\s+clients:update
5353
description: "Update OAuth client"
5454
id: heroku:update_oauth_client
5555
severity: Medium
5656
- from: heroku
57-
test: heroku\s*apps:destroy
57+
test: heroku\s+apps:destroy
5858
description: "Permanently destroy an app"
5959
id: heroku:destroy_app
6060
severity: Critical
6161
- from: heroku
62-
test: heroku\s*apps:leave
62+
test: heroku\s+apps:leave
6363
description: "Remove yourself from a team app"
6464
id: heroku:remove_yourself_from_app
6565
severity: Medium
6666
- from: heroku
67-
test: heroku\s*apps:rename
67+
test: heroku\s+apps:rename
6868
description: "Rename an app"
6969
id: heroku:rename_app_name
7070
severity: Medium
7171
- from: heroku
72-
test: heroku\s*addons:destroy
72+
test: heroku\s+addons:destroy
7373
description: "Permanently destroy an add-on resource"
7474
id: heroku:destroy_addons
7575
severity: High
7676
- from: heroku
77-
test: heroku\s*addons:detach
77+
test: heroku\s+addons:detach
7878
description: "Detach an existing add-on resource from an app"
7979
id: heroku:detach_addon
8080
severity: Medium
8181
- from: heroku
82-
test: heroku\s*access:remove
82+
test: heroku\s+access:remove
8383
description: "Remove users from a team app"
8484
id: heroku:remove_user_access
8585
severity: High
8686
- from: heroku
87-
test: heroku\s*access:update
87+
test: heroku\s+access:update
8888
description: "Update existing collaborators on an team app"
8989
id: heroku:update_collaborators_access
9090
severity: Medium
9191
- from: heroku
92-
test: heroku\s*repo:reset
92+
test: heroku\s+repo:reset
9393
description: "Reset heroku repo"
9494
id: heroku:reset_repo
9595
severity: High

0 commit comments

Comments
 (0)