@@ -766,6 +766,114 @@ export const hookRules: ReadonlyArray<Rule> = [
766766 }
767767 }
768768
769+ return findings ;
770+ } ,
771+ } ,
772+ {
773+ id : "hooks-file-deletion" ,
774+ name : "Hook Deletes Files" ,
775+ description : "Checks for hooks that delete files, which could destroy work or cover tracks" ,
776+ severity : "high" ,
777+ category : "hooks" ,
778+ check ( file : ConfigFile ) : ReadonlyArray < Finding > {
779+ if ( file . type !== "settings-json" && file . type !== "hook-script" ) return [ ] ;
780+
781+ const findings : Finding [ ] = [ ] ;
782+
783+ const deletePatterns : ReadonlyArray < {
784+ readonly pattern : RegExp ;
785+ readonly description : string ;
786+ } > = [
787+ {
788+ pattern : / \b r m \s + - [ a - z A - Z ] * r [ a - z A - Z ] * f ? \b / g,
789+ description : "Recursive file deletion (rm -rf) — can destroy entire directories" ,
790+ } ,
791+ {
792+ pattern : / \b r m \s + - [ a - z A - Z ] * f \b / g,
793+ description : "Force file deletion (rm -f) — deletes without confirmation" ,
794+ } ,
795+ {
796+ pattern : / \b s h r e d \b / g,
797+ description : "Secure file erasure (shred) — irrecoverable deletion used to cover tracks" ,
798+ } ,
799+ {
800+ pattern : / \b u n l i n k \b / g,
801+ description : "File deletion via unlink" ,
802+ } ,
803+ ] ;
804+
805+ for ( const { pattern, description } of deletePatterns ) {
806+ const matches = findAllMatches ( file . content , pattern ) ;
807+ for ( const match of matches ) {
808+ findings . push ( {
809+ id : `hooks-file-delete-${ match . index } ` ,
810+ severity : "high" ,
811+ category : "hooks" ,
812+ title : `Hook deletes files: ${ match [ 0 ] . trim ( ) } ` ,
813+ description : `${ description } . A hook that deletes files could destroy source code, logs, or evidence of compromise.` ,
814+ file : file . path ,
815+ line : findLineNumber ( file . content , match . index ?? 0 ) ,
816+ evidence : match [ 0 ] . trim ( ) ,
817+ } ) ;
818+ }
819+ }
820+
821+ return findings ;
822+ } ,
823+ } ,
824+ {
825+ id : "hooks-cron-persistence" ,
826+ name : "Hook Installs Cron Job" ,
827+ description : "Checks for hooks that install cron jobs for persistent access" ,
828+ severity : "critical" ,
829+ category : "hooks" ,
830+ check ( file : ConfigFile ) : ReadonlyArray < Finding > {
831+ if ( file . type !== "settings-json" && file . type !== "hook-script" ) return [ ] ;
832+
833+ const findings : Finding [ ] = [ ] ;
834+
835+ const cronPatterns : ReadonlyArray < {
836+ readonly pattern : RegExp ;
837+ readonly description : string ;
838+ } > = [
839+ {
840+ pattern : / \b c r o n t a b \b / g,
841+ description : "Modifies crontab — installs persistent scheduled tasks" ,
842+ } ,
843+ {
844+ pattern : / \/ e t c \/ c r o n / g,
845+ description : "Writes to system cron directory — installs persistent scheduled tasks" ,
846+ } ,
847+ {
848+ pattern : / \b a t \s + - [ a - z ] / g,
849+ description : "Schedules deferred command execution via at" ,
850+ } ,
851+ {
852+ pattern : / \b s y s t e m c t l \s + (?: e n a b l e | s t a r t ) / g,
853+ description : "Enables/starts a systemd service — potential persistence mechanism" ,
854+ } ,
855+ {
856+ pattern : / \b l a u n c h c t l \s + l o a d / g,
857+ description : "Loads a macOS launch agent — persistent background process" ,
858+ } ,
859+ ] ;
860+
861+ for ( const { pattern, description } of cronPatterns ) {
862+ const matches = findAllMatches ( file . content , pattern ) ;
863+ for ( const match of matches ) {
864+ findings . push ( {
865+ id : `hooks-cron-persist-${ match . index } ` ,
866+ severity : "critical" ,
867+ category : "hooks" ,
868+ title : `Hook installs persistence mechanism: ${ match [ 0 ] . trim ( ) } ` ,
869+ description : `${ description } . Hooks should not install persistence mechanisms. This could allow a compromised hook to maintain access even after the session ends.` ,
870+ file : file . path ,
871+ line : findLineNumber ( file . content , match . index ?? 0 ) ,
872+ evidence : match [ 0 ] . trim ( ) ,
873+ } ) ;
874+ }
875+ }
876+
769877 return findings ;
770878 } ,
771879 } ,
0 commit comments