diff --git a/frontend/src/assets/deltaworks-earth-7641871.jpg b/frontend/src/assets/deltaworks-earth-7641871.jpg new file mode 100644 index 0000000..3d364d1 Binary files /dev/null and b/frontend/src/assets/deltaworks-earth-7641871.jpg differ diff --git a/frontend/src/assets/deltaworks-earth-7641876.jpg b/frontend/src/assets/deltaworks-earth-7641876.jpg new file mode 100644 index 0000000..422e762 Binary files /dev/null and b/frontend/src/assets/deltaworks-earth-7641876.jpg differ diff --git a/frontend/src/assets/edenmoon-earth-8463728.png b/frontend/src/assets/edenmoon-earth-8463728.png new file mode 100644 index 0000000..2670d48 Binary files /dev/null and b/frontend/src/assets/edenmoon-earth-8463728.png differ diff --git a/frontend/src/assets/tungnguyen0905-technology-7111765.jpg b/frontend/src/assets/tungnguyen0905-technology-7111765.jpg new file mode 100644 index 0000000..38624a4 Binary files /dev/null and b/frontend/src/assets/tungnguyen0905-technology-7111765.jpg differ diff --git a/frontend/src/components/layout/AppLayout.css b/frontend/src/components/layout/AppLayout.css index 6a566e8..b8da619 100644 --- a/frontend/src/components/layout/AppLayout.css +++ b/frontend/src/components/layout/AppLayout.css @@ -13,7 +13,7 @@ .page-content { flex: 1; min-width: 0; - padding: 32px; + padding: 36px 20px; } @media (max-width: 768px) { diff --git a/frontend/src/components/techniques/allTechniques/TechniqueCard/TechniqueCard.tsx b/frontend/src/components/techniques/allTechniques/TechniqueCard/TechniqueCard.tsx index 4d60d0e..3fe287f 100644 --- a/frontend/src/components/techniques/allTechniques/TechniqueCard/TechniqueCard.tsx +++ b/frontend/src/components/techniques/allTechniques/TechniqueCard/TechniqueCard.tsx @@ -53,7 +53,7 @@ function TechniqueCard({ Open profile diff --git a/frontend/src/components/techniques/specificTechnique/AssociatedSoftware/AssociatedSoftware.css b/frontend/src/components/techniques/specificTechnique/AssociatedSoftware/AssociatedSoftware.css new file mode 100644 index 0000000..0293ac9 --- /dev/null +++ b/frontend/src/components/techniques/specificTechnique/AssociatedSoftware/AssociatedSoftware.css @@ -0,0 +1,37 @@ +.associated-software { + padding: 24px; + + background: var(--surface); + + border: 1px solid var(--border); + border-radius: 8px; +} + +.associated-software-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 12px; +} + +.associated-software-header { + display: flex; + align-items: center; + gap: 12px; + + margin-bottom: 20px; +} + +.associated-software h2 { + margin-bottom: 16px; +} + +.associated-software-card { + padding: 14px; + + border: 1px solid var(--border); + border-radius: 8px; +} + +.associated-software-card + .associated-software-card { + margin-top: 10px; +} \ No newline at end of file diff --git a/frontend/src/components/techniques/specificTechnique/AssociatedSoftware/AssociatedSoftware.tsx b/frontend/src/components/techniques/specificTechnique/AssociatedSoftware/AssociatedSoftware.tsx new file mode 100644 index 0000000..b1e9271 --- /dev/null +++ b/frontend/src/components/techniques/specificTechnique/AssociatedSoftware/AssociatedSoftware.tsx @@ -0,0 +1,34 @@ +import "./AssociatedSoftware.css"; +import { Cog } from "lucide-react"; + +type Software = { + name: string; +}; + +type AssociatedSoftwareProps = { + software: Software[]; +}; + +function AssociatedSoftware({ + software, +}: AssociatedSoftwareProps) { + return ( +
+
+ +

Associated Software

+
+ + {software.map((item) => ( +
+ {item.name} +
+ ))} +
+ ); +} + +export default AssociatedSoftware; \ No newline at end of file diff --git a/frontend/src/components/techniques/specificTechnique/RelatedThreatActors/RelatedThreatActors.css b/frontend/src/components/techniques/specificTechnique/RelatedThreatActors/RelatedThreatActors.css new file mode 100644 index 0000000..25ec2a1 --- /dev/null +++ b/frontend/src/components/techniques/specificTechnique/RelatedThreatActors/RelatedThreatActors.css @@ -0,0 +1,33 @@ +.related-threat-actors { + padding: 24px; + width: 100%; + + background: var(--surface); + + border: 1px solid var(--border); + border-radius: 8px; +} + +.related-threat-actors-header { + display: flex; + align-items: center; + gap: 12px; + + margin-bottom: 20px; +} + +.related-threat-actors h2 { + margin-bottom: 16px; +} + +.related-threat-actor-card { + padding: 16px; + white-space: nowrap; + + border: 1px solid var(--border); + border-radius: 8px; +} + +.related-threat-actor-card + .related-threat-actor-card { + margin-top: 10px; +} \ No newline at end of file diff --git a/frontend/src/components/techniques/specificTechnique/RelatedThreatActors/RelatedThreatActors.tsx b/frontend/src/components/techniques/specificTechnique/RelatedThreatActors/RelatedThreatActors.tsx new file mode 100644 index 0000000..cea2442 --- /dev/null +++ b/frontend/src/components/techniques/specificTechnique/RelatedThreatActors/RelatedThreatActors.tsx @@ -0,0 +1,44 @@ +import "./RelatedThreatActors.css"; +import { Users } from "lucide-react"; + +type ThreatActor = { + mitreId: string; + name: string; + alias: string; +}; + +type RelatedThreatActorsProps = { + actors: ThreatActor[]; +}; + +function RelatedThreatActors({ + actors, +}: RelatedThreatActorsProps) { + return ( +
+
+ +

Related Threat Actors

+
+ + {actors.map((actor) => ( +
+
+

{actor.name}

+ + {actor.mitreId} + +
+

+ Also known as {actor.alias} +

+
+ ))} +
+ ); +} + +export default RelatedThreatActors; \ No newline at end of file diff --git a/frontend/src/components/techniques/specificTechnique/TechniqueDescription/TechniqueDescription.css b/frontend/src/components/techniques/specificTechnique/TechniqueDescription/TechniqueDescription.css new file mode 100644 index 0000000..efeaa0c --- /dev/null +++ b/frontend/src/components/techniques/specificTechnique/TechniqueDescription/TechniqueDescription.css @@ -0,0 +1,27 @@ +.technique-description { + padding: 24px; + + background: var(--surface); + + border: 1px solid var(--border); + border-radius: 8px; +} + +.technique-description-header { + display: flex; + align-items: center; + gap: 12px; + + margin-bottom: 12px; +} + +.technique-description-header svg { + width: 28px; + height: 28px; +} + +.technique-description p { + color: var(--text-muted); + + line-height: 1.6; +} \ No newline at end of file diff --git a/frontend/src/components/techniques/specificTechnique/TechniqueDescription/TechniqueDescription.tsx b/frontend/src/components/techniques/specificTechnique/TechniqueDescription/TechniqueDescription.tsx new file mode 100644 index 0000000..2e5a72f --- /dev/null +++ b/frontend/src/components/techniques/specificTechnique/TechniqueDescription/TechniqueDescription.tsx @@ -0,0 +1,26 @@ +import { FileText } from "lucide-react"; +import "./TechniqueDescription.css"; + +type TechniqueDescriptionProps = { + description: string; +}; + +function TechniqueDescription({ + description, +}: TechniqueDescriptionProps) { + return ( +
+
+ + +

Description

+
+ +
+

{description}

+
+
+ ); +} + +export default TechniqueDescription; diff --git a/frontend/src/components/techniques/specificTechnique/TechniqueDetailedHeader/TechniqueDetailedHeader.css b/frontend/src/components/techniques/specificTechnique/TechniqueDetailedHeader/TechniqueDetailedHeader.css new file mode 100644 index 0000000..a36c055 --- /dev/null +++ b/frontend/src/components/techniques/specificTechnique/TechniqueDetailedHeader/TechniqueDetailedHeader.css @@ -0,0 +1,78 @@ +.technique-detailed-header { + display: flex; + flex-direction: column; + gap: 14px; +} + +.technique-detailed-back-link { + display: flex; + align-items: center; + gap: 8px; + + color: var(--accent); + text-decoration: none; + + font-size: 14px; + font-weight: 600; +} + +.technique-detailed-type { + color: var(--text-muted); + + font-size: 12px; + font-weight: 700; + + letter-spacing: 0.08em; + color: #6d3bf7; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.08em; +} + +.technique-detailed-title-row { + display: flex; + align-items: center; + gap: 16px; +} + +.technique-detailed-title-row h1 { + font-size: 56px; + font-weight: 800; +} + +.technique-detailed-title-row p { + font-size: 20px; +} + +.technique-detailed-favorite-button { + background: transparent; + border: none; + + color: var(--text); + + cursor: pointer; +} + +.technique-detailed-tags { + display: flex; + gap: 12px; + flex-wrap: wrap; +} + +.technique-tactic-tag { + padding: 8px 16px; + + border-radius: 999px; + + background: rgba(0, 0, 0, 0.946);; + color: #6d3bf7; +} + +.technique-platform-tag { + padding: 8px 16px; + + border-radius: 999px; + + background: rgba(0, 0, 0, 0.85); + color: #c7ddff; +} \ No newline at end of file diff --git a/frontend/src/components/techniques/specificTechnique/TechniqueDetailedHeader/TechniqueDetailedHeader.tsx b/frontend/src/components/techniques/specificTechnique/TechniqueDetailedHeader/TechniqueDetailedHeader.tsx new file mode 100644 index 0000000..ee24631 --- /dev/null +++ b/frontend/src/components/techniques/specificTechnique/TechniqueDetailedHeader/TechniqueDetailedHeader.tsx @@ -0,0 +1,66 @@ +import { ArrowLeft, Star } from "lucide-react"; +import { Link } from "react-router-dom"; + +import "./TechniqueDetailedHeader.css"; + +type TechniqueDetailedHeaderProps = { + mitreId: string; + name: string; + tactic: string; + platforms: string[]; +}; + +function TechniqueDetailedHeader({ + mitreId, + name, + tactic, + platforms, +}: TechniqueDetailedHeaderProps) { + return ( +
+ + + Back to Techniques + + + + TECHNIQUE + + +
+
+

{mitreId}

+

{name}

+
+ + +
+ +
+ + {tactic} + + + {platforms.map((platform) => ( + + {platform} + + ))} +
+
+ ); +} + +export default TechniqueDetailedHeader; \ No newline at end of file diff --git a/frontend/src/components/techniques/specificTechnique/TechniqueOverview/TechniqueOverview.css b/frontend/src/components/techniques/specificTechnique/TechniqueOverview/TechniqueOverview.css new file mode 100644 index 0000000..ad25410 --- /dev/null +++ b/frontend/src/components/techniques/specificTechnique/TechniqueOverview/TechniqueOverview.css @@ -0,0 +1,28 @@ +.technique-overview { + padding: 24px; + + background: var(--surface); + + border: 1px solid var(--border); + border-radius: 12px; + + height: fit-content; +} + +.technique-overview h2 { + margin-bottom: 20px; +} + +.technique-overview-content { + display: grid; + grid-template-columns: 140px 1fr; + gap: 14px; +} + +.technique-overview-label { + color: var(--text-muted); +} + +.technique-overview-value { + font-weight: 600; +} \ No newline at end of file diff --git a/frontend/src/components/techniques/specificTechnique/TechniqueOverview/TechniqueOverview.tsx b/frontend/src/components/techniques/specificTechnique/TechniqueOverview/TechniqueOverview.tsx new file mode 100644 index 0000000..498951d --- /dev/null +++ b/frontend/src/components/techniques/specificTechnique/TechniqueOverview/TechniqueOverview.tsx @@ -0,0 +1,37 @@ +import "./TechniqueOverview.css"; + +type TechniqueOverviewProps = { + mitreId: string; + name: string; + tactic: string; + platforms: string[]; +}; + +function TechniqueOverview({ + mitreId, + name, + tactic, + platforms, +}: TechniqueOverviewProps) { + return ( + + ); +} + +export default TechniqueOverview; \ No newline at end of file diff --git a/frontend/src/components/techniques/specificTechnique/TechniqueSubTechnique/SubTechniques.css b/frontend/src/components/techniques/specificTechnique/TechniqueSubTechnique/SubTechniques.css new file mode 100644 index 0000000..86339fd --- /dev/null +++ b/frontend/src/components/techniques/specificTechnique/TechniqueSubTechnique/SubTechniques.css @@ -0,0 +1,36 @@ +.sub-techniques { + padding: 24px; + + background: var(--surface); + + border: 1px solid var(--border); + border-radius: 8px; +} + +.sub-techniques-header { + display: flex; + align-items: center; + gap: 12px; + + margin-bottom: 20px; +} + +.sub-techniques h2 { + margin-bottom: 16px; +} + +.sub-technique-card { + display: flex; + justify-content: space-between; + align-items: center; +} + +.sub-technique-left { + display: flex; + align-items: center; + gap: 12px; +} + +.sub-technique-card + .sub-technique-card { + margin-top: 10px; +} \ No newline at end of file diff --git a/frontend/src/components/techniques/specificTechnique/TechniqueSubTechnique/SubTechniques.tsx b/frontend/src/components/techniques/specificTechnique/TechniqueSubTechnique/SubTechniques.tsx new file mode 100644 index 0000000..04640ae --- /dev/null +++ b/frontend/src/components/techniques/specificTechnique/TechniqueSubTechnique/SubTechniques.tsx @@ -0,0 +1,44 @@ +import "./SubTechniques.css"; +import { Network, FileText, ChevronRight } from "lucide-react"; + +type SubTechnique = { + mitreId: string; + name: string; +}; + +type SubTechniquesProps = { + subTechniques: SubTechnique[]; +}; + +function SubTechniques({ + subTechniques, +}: SubTechniquesProps) { + return ( +
+
+ + +

Sub-techniques

+
+ + {subTechniques.map((technique) => ( +
+
+ +
+

{technique.mitreId}

+

{technique.name}

+
+
+ + +
+ ))} +
+ ); +} + +export default SubTechniques; \ No newline at end of file diff --git a/frontend/src/pages/Techniques/TechniqueDetailed.css b/frontend/src/pages/Techniques/TechniqueDetailed.css new file mode 100644 index 0000000..d190361 --- /dev/null +++ b/frontend/src/pages/Techniques/TechniqueDetailed.css @@ -0,0 +1,75 @@ +.technique-background { + margin: -32px; + padding: 32px; + + background-image: url("../../assets/deltaworks-earth-7641871.jpg"); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + +} + +.technique-detailed-page { + padding: 32px; + max-width: 1400px; + + margin: 0 auto; + + display: flex; + flex-direction: column; + gap: 24px; + width: min(1400px, calc(100% - 64px)); +} + +.technique-detailed-top { + position: relative; + + display: grid; + grid-template-columns: 2fr 1fr; + gap: 24px; + + overflow: hidden; +} + +.technique-detailed-tags { + display: flex; + gap: 12px; + flex-wrap: wrap; + + margin-top: 16px; + margin-bottom: 24px; +} + + +.technique-description { + width: 100%; +} + +.technique-detailed-middle { + width: 100%; + + display: grid; + grid-template-columns: 2fr 1fr; + + gap: 24px; +} + +.technique-detailed-left-column { + width: 100%; + min-width: 675px; + + display: flex; + flex-direction: column; + + gap: 24px; +} + +.technique-detailed-right-column { + width: 100%; + min-width: 500px; + + display: flex; + flex-direction: column; + + gap: 24px; +} diff --git a/frontend/src/pages/Techniques/TechniqueDetailed.tsx b/frontend/src/pages/Techniques/TechniqueDetailed.tsx new file mode 100644 index 0000000..3668608 --- /dev/null +++ b/frontend/src/pages/Techniques/TechniqueDetailed.tsx @@ -0,0 +1,131 @@ +import TechniqueDetailedHeader from "../../components/techniques/specificTechnique/TechniqueDetailedHeader/TechniqueDetailedHeader"; +import TechniqueDescription from "../../components/techniques/specificTechnique/TechniqueDescription/TechniqueDescription"; +import TechniqueOverview from "../../components/techniques/specificTechnique/TechniqueOverview/TechniqueOverview.tsx"; +import SubTechniques from "../../components/techniques/specificTechnique/TechniqueSubTechnique/SubTechniques"; +import RelatedThreatActors from "../../components/techniques/specificTechnique/RelatedThreatActors/RelatedThreatActors"; +import AssociatedSoftware from "../../components/techniques/specificTechnique/AssociatedSoftware/AssociatedSoftware.tsx"; +import worldMap from "../../assets/tungnguyen0905-technology-7111765.jpg"; +import "./TechniqueDetailed.css"; + +const subTechniques = [ + { + mitreId: "T1566.001", + name: "Spearphishing Attachment", + }, + { + mitreId: "T1566.002", + name: "Spearphishing Link", + }, + { + mitreId: "T1566.003", + name: "Spearphishing via Service", + }, +]; + +const actors = [ + { + mitreId: "G0016", + name: "APT29", + alias: "Cozy Bear", + }, + { + mitreId: "G0032", + name: "Lazarus Group", + alias: "Hidden Cobra", + }, + { + mitreId: "G0034", + name: "Sandworm Team", + alias: "BlackEnergy", + }, + { + mitreId: "G1015", + name: "Scattered Spider", + alias: "UNC3944", + }, +]; + +const software = [ + { + name: "Cobalt Strike", + }, + { + name: "PlugX", + }, + { + name: "PowerShell Empire", + }, + { + name: "WellMess", + }, +]; + +function TechniqueDetailed() { + return ( +
+
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ + + +
+ +
+ +
+
+
+
+
+ ); +} +export default TechniqueDetailed; \ No newline at end of file diff --git a/frontend/src/routes/AppRoutes.tsx b/frontend/src/routes/AppRoutes.tsx index 5cbdced..bcbb057 100644 --- a/frontend/src/routes/AppRoutes.tsx +++ b/frontend/src/routes/AppRoutes.tsx @@ -9,6 +9,7 @@ import Techniques from "../pages/Techniques/Techniques"; import AppLayout from "../components/layout/AppLayout"; import TacticDetailed from "../pages/Tactics/TacticDetailed"; import ThreatActorDetailed from "../pages/ThreatActors/ThreatActorDetailed"; +import TechniqueDetailed from "../pages/Techniques/TechniqueDetailed"; /** * Håndterer navigasjon mellom de ulike pages på nettsiden @@ -34,6 +35,7 @@ function AppRoutes() { }/> } /> } /> + } />