Skip to content

Commit

Permalink
Merge pull request #161 from deggja/feat/score
Browse files Browse the repository at this point in the history
feat: update scoring logic
  • Loading branch information
deggja committed Aug 31, 2024
2 parents 2f34050 + 24bac70 commit 487469e
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 56 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ jobs:
cd backend
go mod tidy
- name: Bump version and tag
id: bump_version
run: |
chmod +x .github/scripts/bump_version.sh
.github/scripts/bump_version.sh
# - name: Bump version and tag
# id: bump_version
# run: |
# chmod +x .github/scripts/bump_version.sh
# .github/scripts/bump_version.sh

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
Expand Down
2 changes: 1 addition & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ changelog:
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
order: 0
- title: "Bug fixes"
regexp: '^.*?bug(\([[:word:]]+\))??!?:.+$'
regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
order: 1
- title: "Documentation Updates"
regexp: '^.*?docs(\([[:word:]]+\))??!?:.+$'
Expand Down
11 changes: 6 additions & 5 deletions backend/cmd/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ var scanCmd = &cobra.Command{
fmt.Printf("No pods targeted by policy '%s' in namespace '%s'.\n", policy.GetName(), foundNamespace)
} else {
fmt.Printf("Pods targeted by policy '%s' in namespace '%s':\n", policy.GetName(), foundNamespace)
fmt.Println(createTargetPodsTable(pods, foundNamespace))
fmt.Println(createTargetPodsTable(pods))
}
}
return
Expand Down Expand Up @@ -92,7 +92,7 @@ var scanCmd = &cobra.Command{
fmt.Printf("No pods targeted by cluster wide policy '%s'.\n", policy.GetName())
} else {
fmt.Printf("Pods targeted by cluster wide policy '%s':\n", policy.GetName())
fmt.Println(createTargetPodsTable(pods, ""))
fmt.Println(createTargetPodsTable(pods))
}
}
} else {
Expand All @@ -106,7 +106,7 @@ var scanCmd = &cobra.Command{
fmt.Printf("No pods targeted by policy '%s' in namespace '%s'.\n", policy.GetName(), foundNamespace)
} else {
fmt.Printf("Pods targeted by policy '%s' in namespace '%s':\n", policy.GetName(), foundNamespace)
fmt.Println(createTargetPodsTable(pods, foundNamespace))
fmt.Println(createTargetPodsTable(pods))
}
}
return
Expand Down Expand Up @@ -141,14 +141,15 @@ var scanCmd = &cobra.Command{
} else {
// Handle the cluster wide scan result; skip further scanning if all pods are protected
if clusterwideScanResult.AllPodsProtected {
fmt.Println("All pods are protected by cluster wide cilium policies.\nYour Netfetch security score is: 42/42")
fmt.Println("All pods are protected by cluster wide cilium policies.\nYour Netfetch security score is: 100/100")
return
}
handleScanResult(clusterwideScanResult)
}
}

// Proceed with normal Cilium network policy scan
fmt.Println("Running cilium network policies scan...")
ciliumScanResult, err := k8s.ScanCiliumNetworkPolicies(namespace, dryRun, false, true, true, true)
if err != nil {
fmt.Println("Error during Cilium network policies scan:", err)
Expand All @@ -172,7 +173,7 @@ var (
)

// Function to create a table for pods
func createTargetPodsTable(pods [][]string, namespace string) string {
func createTargetPodsTable(pods [][]string) string {
t := table.New().
Border(lipgloss.NormalBorder()).
BorderStyle(tableBorderStyle).
Expand Down
37 changes: 23 additions & 14 deletions backend/pkg/k8s/cilium-scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,18 @@ func fetchCiliumPolicies(dynamicClient dynamic.Interface, nsName string, writer
return unstructuredPolicies, hasDenyAll, nil
}

// helper function to ensure pods are not added to list multiple times
func addUniquePodDetail(podDetails []string, detail string) []string {
for _, d := range podDetails {
if d == detail {
return podDetails // pod already in the list.
}
}
return append(podDetails, detail) // add pod if its not in list
}

// determinePodCoverage identifies unprotected pods in a namespace based on the fetched Cilium policies.
func determinePodCoverage(clientset *kubernetes.Clientset, nsName string, policies []*unstructured.Unstructured, hasDenyAll bool, writer *bufio.Writer, scanResult *ScanResult) ([]string, error) {
func determinePodCoverage(clientset *kubernetes.Clientset, nsName string, policies []*unstructured.Unstructured, hasDenyAll bool, writer *bufio.Writer) ([]string, error) {
unprotectedPods := []string{}

pods, err := clientset.CoreV1().Pods(nsName).List(context.TODO(), metav1.ListOptions{})
Expand All @@ -194,16 +204,15 @@ func determinePodCoverage(clientset *kubernetes.Clientset, nsName string, polici
continue
}
podIdentifier := fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)
if _, exists := globallyProtectedPods[podIdentifier]; !exists {
if !IsPodProtected(writer, clientset, pod, policies, hasDenyAll, globallyProtectedPods) {
unprotectedPodDetails := fmt.Sprintf("%s %s %s", pod.Namespace, pod.Name, pod.Status.PodIP)
unprotectedPods = append(unprotectedPods, unprotectedPodDetails)
scanResult.UnprotectedPods = append(scanResult.UnprotectedPods, unprotectedPodDetails)
} else {
globallyProtectedPods[podIdentifier] = struct{}{} // Mark the pod as protected globally
}
}
}
if _, exists := globallyProtectedPods[podIdentifier]; !exists {
if !IsPodProtected(writer, clientset, pod, policies, hasDenyAll, globallyProtectedPods) {
unprotectedPodDetails := fmt.Sprintf("%s %s %s", pod.Namespace, pod.Name, pod.Status.PodIP)
unprotectedPods = addUniquePodDetail(unprotectedPods, unprotectedPodDetails)
} else {
globallyProtectedPods[podIdentifier] = struct{}{} // Mark the pod as protected globally
}
}
}

return unprotectedPods, nil
}
Expand All @@ -215,7 +224,7 @@ func processNamespacePoliciesCilium(dynamicClient dynamic.Interface, clientset *
return err
}

unprotectedPods, err := determinePodCoverage(clientset, nsName, ciliumPolicies, hasDenyAll, writer, scanResult)
unprotectedPods, err := determinePodCoverage(clientset, nsName, ciliumPolicies, hasDenyAll, writer)
if err != nil {
return err
}
Expand Down Expand Up @@ -356,7 +365,7 @@ func ScanCiliumNetworkPolicies(specificNamespace string, dryRun bool, returnResu

if printScore {
// Print the final score
fmt.Printf("\nYour Netfetch security score is: %d/42\n", score)
fmt.Printf("\nYour Netfetch security score is: %d/100\n", score)
}

hasStartedCiliumScan = false
Expand Down Expand Up @@ -563,7 +572,7 @@ func ScanCiliumClusterwideNetworkPolicies(dynamicClient dynamic.Interface, print
UserDeniedPolicies: false,
AllPodsProtected: false,
HasDenyAll: []string{},
Score: 0, // or some initial value
Score: 50, // or some initial value
}

defaultDenyAllFound, appliesToEntireCluster, partialDenyAllPolicies, partialDenyAllFound := analyzeClusterwidePolicies(unstructuredPolicies)
Expand Down
26 changes: 15 additions & 11 deletions backend/pkg/k8s/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ func ScanNetworkPolicies(specificNamespace string, dryRun bool, returnResult boo

if printScore {
// Print the final score
fmt.Printf("\nYour Netfetch security score is: %d/42\n", score)
fmt.Printf("\nYour Netfetch security score is: %d/100\n", score)
}

hasStartedNativeScan = false
Expand Down Expand Up @@ -397,20 +397,24 @@ func IsSystemNamespace(namespace string) bool {

// Scoring logic
func CalculateScore(hasPolicies bool, hasDenyAll bool, unprotectedPodsCount int) int {
score := 42 // Start with the highest score
score := 50 // Start with a base score of 50

if !hasPolicies {
score -= 20
}
if hasDenyAll {
score += 20 // Add 20 points for having deny-all policies
} else if !hasPolicies {
score -= 20 // Subtract 20 points if there are no policies at all
}

// Deduct score based on the number of unprotected pods
score -= unprotectedPodsCount
// Deduct score based on the number of unprotected pods
score -= unprotectedPodsCount

if score < 1 {
score = 1 // Minimum score
}
if score > 100 {
score = 100
} else if score < 1 {
score = 1
}

return score
return score
}

// INTERACTIVE DASHBOARD LOGIC
Expand Down
40 changes: 20 additions & 20 deletions backend/pkg/k8s/scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,29 +79,29 @@ func TestIsSystemNamespace(t *testing.T) {
}

func TestCalculateScore(t *testing.T) {
// Test case 1: All conditions met, maximum score expected
score1 := CalculateScore(true, true, 0)
if score1 != 42 {
t.Fatalf("Expected score to be 42, got %d", score1)
}
// Test case 1: Policies exist, deny all exists, no unprotected pods
score1 := CalculateScore(true, true, 0)
if score1 != 70 { // 50 base + 20 for deny-all
t.Fatalf("Expected score to be 70, got %d", score1)
}

// Test case 2: No policies, no deny all, 5 unprotected pods
score2 := CalculateScore(false, false, 5)
if score2 != 17 {
t.Fatalf("Expected score to be 17, got %d", score2)
}
// Test case 2: No policies, no deny all, 5 unprotected pods
score2 := CalculateScore(false, false, 5)
if score2 != 25 { // 50 base - 20 for no policies - 5 for unprotected pods
t.Fatalf("Expected score to be 25, got %d", score2)
}

// Test case 3: No policies, no deny all, no unprotected pods
score3 := CalculateScore(false, false, 0)
if score3 != 22 {
t.Fatalf("Expected score to be 1, got %d", score3)
}
// Test case 3: No policies, no deny all, no unprotected pods
score3 := CalculateScore(false, false, 0)
if score3 != 30 { // 50 base - 20 for no policies
t.Fatalf("Expected score to be 30, got %d", score3)
}

// Test case 4: Policies exist, deny all exists, no unprotected pods
score4 := CalculateScore(true, true, 0)
if score4 != 42 {
t.Fatalf("Expected score to be 42, got %d", score4)
}
// Test case 4: Policies exist, deny all exists, no unprotected pods (repeat of case 1 for consistency)
score4 := CalculateScore(true, true, 0)
if score4 != 70 {
t.Fatalf("Expected score to be 70, got %d", score4)
}
}

func TestGetPodInfo(t *testing.T) {
Expand Down

0 comments on commit 487469e

Please sign in to comment.