117 lines
2.1 KiB
Go
117 lines
2.1 KiB
Go
/*
|
|
©AngelaMos | 2026
|
|
cron_test.go
|
|
*/
|
|
|
|
package scanner
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/CarterPerez-dev/sentinel/pkg/types"
|
|
)
|
|
|
|
func TestCronScanner_CleanCrontab(t *testing.T) {
|
|
c := &CronScanner{}
|
|
path := filepath.Join(
|
|
testdataDir(), "cron", "clean-crontab",
|
|
)
|
|
|
|
findings := c.scanCrontab(path)
|
|
if len(findings) > 0 {
|
|
t.Errorf(
|
|
"clean crontab produced %d findings, want 0",
|
|
len(findings),
|
|
)
|
|
for _, f := range findings {
|
|
t.Logf(" finding: %s (%s)", f.Title, f.Evidence)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCronScanner_MaliciousCrontab(t *testing.T) {
|
|
c := &CronScanner{}
|
|
path := filepath.Join(
|
|
testdataDir(), "cron", "malicious-crontab",
|
|
)
|
|
|
|
findings := c.scanCrontab(path)
|
|
for _, f := range findings {
|
|
t.Logf(
|
|
"finding: sev=%s title=%s evidence=%s",
|
|
f.Severity, f.Title, f.Evidence,
|
|
)
|
|
}
|
|
if len(findings) < 3 {
|
|
t.Fatalf(
|
|
"malicious crontab: got %d findings, want >= 3",
|
|
len(findings),
|
|
)
|
|
}
|
|
|
|
hasCritical := false
|
|
hasHigh := false
|
|
for _, f := range findings {
|
|
if f.Severity == types.SeverityCritical {
|
|
hasCritical = true
|
|
}
|
|
if f.Severity == types.SeverityHigh {
|
|
hasHigh = true
|
|
}
|
|
}
|
|
|
|
if !hasCritical {
|
|
t.Error("expected critical finding for reverse shell")
|
|
}
|
|
if !hasHigh {
|
|
t.Error("expected high finding for curl|bash")
|
|
}
|
|
}
|
|
|
|
func TestExtractCronCommand(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
line string
|
|
want string
|
|
}{
|
|
{
|
|
name: "system cron with user field",
|
|
line: "0 3 * * * root /usr/sbin/logrotate /etc/logrotate.conf",
|
|
want: "/usr/sbin/logrotate /etc/logrotate.conf",
|
|
},
|
|
{
|
|
name: "at-style entry",
|
|
line: "@reboot root /usr/bin/startup.sh",
|
|
want: "root /usr/bin/startup.sh",
|
|
},
|
|
{
|
|
name: "environment variable",
|
|
line: "SHELL=/bin/bash",
|
|
want: "",
|
|
},
|
|
{
|
|
name: "comment",
|
|
line: "# some comment",
|
|
want: "",
|
|
},
|
|
{
|
|
name: "empty",
|
|
line: "",
|
|
want: "",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := extractCronCommand(tt.line)
|
|
if got != tt.want {
|
|
t.Errorf(
|
|
"extractCronCommand(%q) = %q, want %q",
|
|
tt.line, got, tt.want,
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|