Skip to content
Merged
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
55 changes: 39 additions & 16 deletions internal/api/web/app.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const nodeColors = {
Internet: "#ff6b6b",
Network: "#4dabf7",
Workload: "#51cf66",
Identity: "#ffd43b",
Datastore: "#845ef7",
Finding: "#ff922b",
Control: "#868e96",
Internet: "#f87171",
Network: "#22d3ee",
Workload: "#34d399",
Identity: "#fbbf24",
Datastore: "#a855f7",
Finding: "#fb923c",
Control: "#94a3b8",
};

let network = null;
Expand Down Expand Up @@ -595,7 +595,7 @@ function renderFindings(findings, partial) {
<div class="title">${f.name}</div>
<div class="meta">${props.description || props.title || ""}</div>
${pathLine}
<div class="meta">${item.affected_resource_name || "Unknown resource"}</div>
<div class="resource">${item.affected_resource_name || "Unknown resource"}</div>
</article>
`;
}).join("") + partialNote;
Expand All @@ -606,12 +606,23 @@ function renderFindings(findings, partial) {
}

function toVisNodes(nodes) {
return nodes.map((n) => ({
id: n.id,
label: `${n.name}\n(${n.type})`,
color: nodeColors[n.type] || "#adb5bd",
font: { color: "#f8f9fa", size: 12 },
}));
return nodes.map((n) => {
const color = nodeColors[n.type] || "#94a3b8";
return {
id: n.id,
label: n.name,
title: n.type,
borderWidth: 2,
color: {
background: "#0d1420",
border: color,
highlight: { background: "#132033", border: "#22d3ee" },
hover: { background: "#111827", border: color },
},
font: { color: "#e2e8f0", size: 13, face: "IBM Plex Sans, system-ui, sans-serif" },
shadow: { enabled: true, color: color, size: 16, x: 0, y: 0 },
};
});
}

function toVisEdges(edges) {
Expand All @@ -621,8 +632,15 @@ function toVisEdges(edges) {
label: e.type,
title: (e.properties && e.properties.reason) || e.type,
arrows: "to",
color: { color: "#495057" },
font: { align: "middle", size: 10, color: "#adb5bd" },
color: { color: "rgba(34, 211, 238, 0.45)", highlight: "#22d3ee", hover: "#a5f3fc" },
font: {
align: "middle",
size: 10,
face: "IBM Plex Mono, ui-monospace, monospace",
color: "#94a3b8",
strokeWidth: 4,
strokeColor: "#070b12",
},
}));
}

Expand All @@ -636,8 +654,13 @@ function renderGraph(nodes, edges) {
const options = {
physics: { stabilization: true },
interaction: { hover: true },
nodes: { shape: "dot", size: 16 },
edges: { width: 1.5, smooth: { type: "continuous" } },
};
network = new vis.Network(container, graphData, options);
network.once("stabilizationIterationsDone", () => {
network.fit({ animation: false, padding: 72 });
});
network.on("click", async (params) => {
if (!params.nodes.length) return;
const nodeID = params.nodes[0];
Expand Down
14 changes: 14 additions & 0 deletions internal/api/web/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 61 additions & 9 deletions internal/api/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,31 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenSourceOM Console</title>
<link rel="icon" href="/ui/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="/ui/styles.css" />
<script src="/ui/vis-network.min.js"></script>
</head>
<body>
<header>
<div>
<h1>OpenSourceOM</h1>
<p class="tagline">Graph-native cloud security</p>
<div class="brand">
<svg class="logo-icon" width="40" height="40" viewBox="0 0 32 32" fill="none" aria-hidden="true">
<rect width="32" height="32" rx="8" fill="#0D1420" stroke="#1E293B" />
<circle cx="16" cy="16" r="5" stroke="url(#brandGrad)" stroke-width="2" />
<circle cx="9" cy="10" r="2" fill="#6366F1" />
<circle cx="23" cy="10" r="2" fill="#A855F7" />
<circle cx="23" cy="22" r="2" fill="#22D3EE" />
<circle cx="9" cy="22" r="2" fill="#F87171" />
<defs>
<linearGradient id="brandGrad" x1="8" y1="8" x2="24" y2="24" gradientUnits="userSpaceOnUse">
<stop stop-color="#22D3EE" />
<stop offset="1" stop-color="#6366F1" />
</linearGradient>
</defs>
</svg>
<div class="brand-copy">
<h1 class="brand-name">OpenSource<span class="brand-accent">OM</span></h1>
<p class="tagline">Graph-native cloud security</p>
</div>
</div>
<div id="account" class="account hidden">
<span id="account-label"></span>
Expand All @@ -32,6 +49,24 @@ <h1>OpenSourceOM</h1>
</header>

<section id="gate" class="gate">
<div class="gate-brand">
<svg class="logo-icon" width="64" height="64" viewBox="0 0 32 32" fill="none" aria-hidden="true">
<rect width="32" height="32" rx="8" fill="#0D1420" stroke="#1E293B" />
<circle cx="16" cy="16" r="5" stroke="url(#gateGrad)" stroke-width="2" />
<circle cx="9" cy="10" r="2" fill="#6366F1" />
<circle cx="23" cy="10" r="2" fill="#A855F7" />
<circle cx="23" cy="22" r="2" fill="#22D3EE" />
<circle cx="9" cy="22" r="2" fill="#F87171" />
<defs>
<linearGradient id="gateGrad" x1="8" y1="8" x2="24" y2="24" gradientUnits="userSpaceOnUse">
<stop stop-color="#22D3EE" />
<stop offset="1" stop-color="#6366F1" />
</linearGradient>
</defs>
</svg>
<p class="brand-name">OpenSource<span class="brand-accent">OM</span></p>
<p class="gate-tag">Graph-native cloud security</p>
</div>
<h2 id="gate-title">Sign in</h2>
<p id="gate-message">Checking session…</p>
<div id="gate-actions" class="gate-actions"></div>
Expand All @@ -54,17 +89,34 @@ <h2>Findings</h2>
<section class="graph-panel">
<div class="graph-toolbar">
<h2>Graph Explorer</h2>
<select id="query-select">
<option value="">Full graph snapshot</option>
</select>
<button id="rules-btn" type="button">Run CSPM rules</button>
<button id="refresh-btn" type="button">Refresh</button>
<div class="toolbar-actions">
<select id="query-select" aria-label="Graph query">
<option value="">Full graph snapshot</option>
</select>
<button id="rules-btn" type="button">Run CSPM rules</button>
<button id="refresh-btn" type="button">Refresh</button>
</div>
</div>
<ul class="legend" aria-label="Node types">
<li><span class="swatch internet"></span>Internet</li>
<li><span class="swatch network"></span>Network</li>
<li><span class="swatch workload"></span>Workload</li>
<li><span class="swatch identity"></span>Identity</li>
<li><span class="swatch datastore"></span>Datastore</li>
<li><span class="swatch finding"></span>Finding</li>
<li><span class="swatch control"></span>Control</li>
</ul>
<p id="console-error" class="console-error hidden" role="alert"></p>
<p id="partial-note" class="meta hidden"></p>
<div id="path-detail" class="detail-panel hidden"></div>
<div id="blast-radius" class="blast-radius hidden"></div>
<div id="graph"></div>
<div class="graph-stage">
<div id="graph"></div>
<span class="frame tl" aria-hidden="true"></span>
<span class="frame tr" aria-hidden="true"></span>
<span class="frame bl" aria-hidden="true"></span>
<span class="frame br" aria-hidden="true"></span>
</div>
</section>
</main>

Expand Down
Loading
Loading