Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
589 changes: 81 additions & 508 deletions frontend/app/page.tsx

Large diffs are not rendered by default.

462 changes: 34 additions & 428 deletions frontend/app/simulator/page.tsx

Large diffs are not rendered by default.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to look through this. I don't think that the score that's computed for security policy changes is a good reflection of the security policy for the repository.

Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Badge } from "@/components/ui/badge"
import { Progress } from "@/components/ui/progress"
import { compareJsonObjects, countChanges } from "@/lib/json-diff"
import type { Commit } from "@/lib/types"
import { compareJsonObjects, countChanges, type DiffResult, type DiffEntry } from "@/lib/json-diff"
import type { Commit, SecurityEvent, JsonObject, JsonValue } from "@/lib/types"
import { motion } from "framer-motion"

interface CommitAnalysisProps {
Expand All @@ -26,18 +26,6 @@ interface CommitAnalysisProps {
selectedFile: string
}

interface SecurityEvent {
commit: string
date: string
author: string
message: string
type: "security_enhancement" | "security_degradation" | "policy_change" | "principal_change" | "expiration_change"
severity: "critical" | "high" | "medium" | "low"
description: string
details: string
impact: string
}

interface SecurityTrend {
metric: string
trend: "improving" | "declining" | "stable"
Expand Down Expand Up @@ -103,13 +91,16 @@ export default function CommitAnalysis({ commits, isLoading, selectedFile }: Com
}
}, [commits])

const analyzeSecurityEvents = (diff: any, commit: Commit): SecurityEvent[] => {
const analyzeSecurityEvents = (diff: DiffResult | DiffEntry | null, commit: Commit): SecurityEvent[] => {
const events: SecurityEvent[] = []

const traverseChanges = (obj: any, path = "") => {
// If diff is null or singular DiffEntry (root change), we might need to handle it.
// Assuming structure is mostly Record<string, DiffEntry> for traversal.

const traverseChanges = (obj: Record<string, DiffEntry> | undefined, path = "") => {
if (!obj) return

Object.entries(obj).forEach(([key, value]: [string, any]) => {
Object.entries(obj).forEach(([key, value]) => {
const currentPath = path ? `${path}.${key}` : key
const pathLower = currentPath.toLowerCase()

Expand All @@ -119,8 +110,8 @@ export default function CommitAnalysis({ commits, isLoading, selectedFile }: Com
// Expiration changes
if (pathLower.includes("expires")) {
if (value.status === "changed") {
const oldDate = new Date(value.oldValue)
const newDate = new Date(value.value)
const oldDate = new Date(String(value.oldValue))
const newDate = new Date(String(value.value))
const extended = newDate > oldDate

event = {
Expand All @@ -141,7 +132,7 @@ export default function CommitAnalysis({ commits, isLoading, selectedFile }: Com

// Threshold changes
else if (pathLower.includes("threshold")) {
if (value.status === "changed") {
if (value.status === "changed" && typeof value.value === 'number' && typeof value.oldValue === 'number') {
const increased = value.value > value.oldValue
event = {
commit: commit.hash.substring(0, 8),
Expand Down Expand Up @@ -230,15 +221,21 @@ export default function CommitAnalysis({ commits, isLoading, selectedFile }: Com
})
}

traverseChanges(diff)
if (diff && !('status' in diff)) {
traverseChanges(diff as DiffResult)
} else if (diff && 'status' in diff && (diff as DiffEntry).children) {
// If root is a DiffEntry with children
traverseChanges((diff as DiffEntry).children)
}

return events
}

const calculateSecurityScore = (data: any, diff: any): number => {
const calculateSecurityScore = (data: JsonObject, diff: DiffResult | DiffEntry | null): number => {
let score = 50 // Base score

// Positive factors
if (data.expires) {
if (data.expires && typeof data.expires === 'string') {
const expiryDate = new Date(data.expires)
const now = new Date()
const daysUntilExpiry = Math.floor((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24))
Expand All @@ -249,23 +246,23 @@ export default function CommitAnalysis({ commits, isLoading, selectedFile }: Com
}

// Threshold analysis
if (data.roles) {
Object.values(data.roles).forEach((role: any) => {
if (role.threshold) {
if (data.roles && typeof data.roles === 'object') {
Object.values(data.roles as JsonObject).forEach((role: any) => {
if (role?.threshold) {
if (role.threshold >= 2) score += 15
else if (role.threshold === 1) score += 5
}
})
}

// Rules analysis
if (data.rules) {
if (data.rules && typeof data.rules === 'object') {
const ruleCount = Object.keys(data.rules).length
score += Math.min(ruleCount * 5, 25) // Max 25 points for rules
}

// Principal diversity
if (data.principals) {
if (data.principals && typeof data.principals === 'object') {
const principalCount = Object.keys(data.principals).length
score += Math.min(principalCount * 3, 15) // Max 15 points for principals
}
Expand All @@ -282,8 +279,8 @@ export default function CommitAnalysis({ commits, isLoading, selectedFile }: Com
const lastCommit = commits[commits.length - 1]

// Principal count trend
const firstPrincipals = firstCommit.data?.principals ? Object.keys(firstCommit.data.principals).length : 0
const lastPrincipals = lastCommit.data?.principals ? Object.keys(lastCommit.data.principals).length : 0
const firstPrincipals = firstCommit.data?.principals && typeof firstCommit.data.principals === 'object' && !Array.isArray(firstCommit.data.principals) ? Object.keys(firstCommit.data.principals).length : 0
const lastPrincipals = lastCommit.data?.principals && typeof lastCommit.data.principals === 'object' && !Array.isArray(lastCommit.data.principals) ? Object.keys(lastCommit.data.principals).length : 0

trends.push({
metric: "Security Principals",
Expand All @@ -294,8 +291,8 @@ export default function CommitAnalysis({ commits, isLoading, selectedFile }: Com
})

// Rules count trend
const firstRules = firstCommit.data?.rules ? Object.keys(firstCommit.data.rules).length : 0
const lastRules = lastCommit.data?.rules ? Object.keys(lastCommit.data.rules).length : 0
const firstRules = firstCommit.data?.rules && typeof firstCommit.data.rules === 'object' && !Array.isArray(firstCommit.data.rules) ? Object.keys(firstCommit.data.rules).length : 0
const lastRules = lastCommit.data?.rules && typeof lastCommit.data.rules === 'object' && !Array.isArray(lastCommit.data.rules) ? Object.keys(lastCommit.data.rules).length : 0

trends.push({
metric: "Security Rules",
Expand All @@ -306,7 +303,7 @@ export default function CommitAnalysis({ commits, isLoading, selectedFile }: Com
})

// Expiration health
if (lastCommit.data?.expires) {
if (lastCommit.data?.expires && typeof lastCommit.data.expires === 'string') {
const expiryDate = new Date(lastCommit.data.expires)
const now = new Date()
const daysUntilExpiry = Math.floor((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24))
Expand Down Expand Up @@ -729,19 +726,26 @@ function SecurityRecommendations({
overallScore: number
isLoading: boolean
}) {
const getRecommendations = () => {
const recommendations = []
interface Recommendation {
priority: "critical" | "high" | "medium" | "low"
title: string
description: string
action: string
}

const getRecommendations = (): Recommendation[] => {
const recommendations: Recommendation[] = []

// Check expiration
const latestCommit = commits[commits.length - 1]
if (latestCommit?.data?.expires) {
if (latestCommit?.data?.expires && typeof latestCommit.data.expires === 'string') {
const expiryDate = new Date(latestCommit.data.expires)
const now = new Date()
const daysUntilExpiry = Math.floor((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24))

if (daysUntilExpiry < 30) {
recommendations.push({
priority: "high" as const,
priority: "high",
title: "Renew Security Metadata",
description: `Security metadata expires in ${daysUntilExpiry} days. Plan renewal soon.`,
action: "Update expiration date and refresh security keys",
Expand All @@ -750,11 +754,11 @@ function SecurityRecommendations({
}

// Check thresholds
if (latestCommit?.data?.roles) {
if (latestCommit?.data?.roles && typeof latestCommit.data.roles === 'object') {
Object.entries(latestCommit.data.roles).forEach(([role, config]: [string, any]) => {
if (config.threshold === 1) {
if (config && typeof config === 'object' && config.threshold === 1) {
recommendations.push({
priority: "medium" as const,
priority: "medium",
title: "Increase Security Threshold",
description: `Role "${role}" only requires 1 signature. Consider increasing for better security.`,
action: "Increase threshold to 2 or more signatures",
Expand All @@ -767,7 +771,7 @@ function SecurityRecommendations({
securityTrends.forEach((trend) => {
if (trend.trend === "declining") {
recommendations.push({
priority: "medium" as const,
priority: "medium",
title: `Address Declining ${trend.metric}`,
description: `${trend.metric} has decreased from ${trend.previous} to ${trend.current}.`,
action: "Review and restore security measures",
Expand All @@ -778,7 +782,7 @@ function SecurityRecommendations({
// Overall score recommendations
if (overallScore < 60) {
recommendations.push({
priority: "high" as const,
priority: "high",
title: "Improve Overall Security",
description: "Security score is below recommended levels.",
action: "Review all security policies and implement missing protections",
Expand All @@ -789,7 +793,7 @@ function SecurityRecommendations({
const criticalEvents = securityEvents.filter((e) => e.severity === "critical")
if (criticalEvents.length > 0) {
recommendations.push({
priority: "critical" as const,
priority: "critical",
title: "Address Critical Security Events",
description: `${criticalEvents.length} critical security events detected.`,
action: "Review and remediate all critical security changes",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { Loader2, GitCompare, AlertTriangle, Minus, Plus, Edit3 } from "lucide-r
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import JsonDiffVisualization from "./json-diff-visualization"
import JsonDiffStats from "./json-diff-stats"
import JsonDiffVisualization from "@/components/features/json/json-diff-visualization"
import JsonDiffStats from "@/components/features/json/json-diff-stats"
import { Button } from "@/components/ui/button"
import type { Commit } from "@/lib/types"
import { useState } from "react"
import JsonTreeView from "./json-tree-view"
import JsonTreeView from "@/components/features/json/json-tree-view"
import { compareJsonObjects, countChanges } from "@/lib/json-diff"
import type { ViewMode } from "@/lib/view-mode-utils"
import { motion } from "framer-motion"
Expand Down
Loading