mirror of
https://github.com/bettercap/bettercap.git
synced 2025-03-12 04:36:03 -07:00
195 lines
6.3 KiB
HTML
195 lines
6.3 KiB
HTML
<head>
|
|
<style> body {
|
|
margin: 0;
|
|
} </style>
|
|
<script src="//unpkg.com/jquery"></script>
|
|
<script src="//unpkg.com/3d-force-graph"></script>
|
|
<script src="//unpkg.com/three"></script>
|
|
<script src="//unpkg.com/three/examples/js/renderers/CSS2DRenderer.js"></script>
|
|
<script src="//unpkg.com/three-spritetext"></script>
|
|
|
|
<style>
|
|
.node-div {
|
|
font-size: 1rem;
|
|
padding: 1px 4px;
|
|
border-radius: 4px;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
user-select: none;
|
|
}
|
|
|
|
.node-data-div {
|
|
background-color: rgba(0, 0, 0, 0.9);
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div id="3d-graph"></div>
|
|
|
|
<script id="vars">
|
|
const typeNodeColors = {
|
|
'ble_server': '#0066ff',
|
|
|
|
'ssid': 'transparent',
|
|
'station': '#ffcc33',
|
|
'access_point': '#ff9900',
|
|
|
|
'endpoint': '#33cc33',
|
|
'gateway': '#006600'
|
|
};
|
|
|
|
const typeColors = {
|
|
'ble_server': '#0066ff',
|
|
|
|
'ssid': '#ffff99',
|
|
'station': '#ffcc33',
|
|
'access_point': '#ff9900',
|
|
|
|
'endpoint': '#33cc33',
|
|
'gateway': '#006600'
|
|
};
|
|
|
|
var targetNode = null;
|
|
</script>
|
|
|
|
<script id="functions">
|
|
function renderNodeHTML(node) {
|
|
const nodeDiv = document.createElement('div');
|
|
switch (node.type) {
|
|
case 'ssid':
|
|
nodeDiv.innerHTML = `<small>${node.entity}</small>`
|
|
break;
|
|
|
|
case 'access_point':
|
|
var ap = node.entity;
|
|
nodeDiv.innerHTML = `
|
|
<center>
|
|
<b>${ap.hostname}</b> (${ap.encryption})
|
|
<br/>
|
|
${ap.mac}
|
|
${ap.vendor? '<br/>(' + ap.vendor + ')' : ''}
|
|
${ap.wps.length? '<br/>' + JSON.stringify(ap.wps) : ''}
|
|
</center>`;
|
|
break;
|
|
|
|
case 'station':
|
|
var sta = node.entity;
|
|
nodeDiv.innerHTML = `
|
|
<center>
|
|
${sta.mac}
|
|
${sta.vendor? '<br/>(' + sta.vendor + ')' : ''}
|
|
</center>`;
|
|
break;
|
|
|
|
case 'ble_server':
|
|
var dev = node.entity;
|
|
nodeDiv.innerHTML = `
|
|
<center>
|
|
${dev.mac}
|
|
${dev.vendor? '<br/>(' + dev.vendor + ')' : ''}
|
|
</center>`;
|
|
break;
|
|
|
|
case 'endpoint':
|
|
var ip = node.entity;
|
|
nodeDiv.innerHTML = `
|
|
<center>
|
|
${ip.hostname? '<b>' + ip.hostname + '</b><br/>' : ''}
|
|
${ip.ipv4? ip.ipv4 + '<br/>' : ''}
|
|
${ip.ipv6? ip.ipv6 + '<br/>' : ''}
|
|
<br/>
|
|
${ip.mac}
|
|
${ip.vendor? '<br/>(' + ip.vendor + ')' : ''}
|
|
${ip.meta.values.length? '<br/>' + JSON.stringify(ip.meta.values) : ''}
|
|
</center>`;
|
|
break;
|
|
|
|
case 'gateway':
|
|
var ip = node.entity;
|
|
nodeDiv.innerHTML = `
|
|
<center>
|
|
${ip.hostname? '<b>' + ip.hostname + '</b><br/>' : ''}
|
|
${ip.ipv4? ip.ipv4 + '<br/>' : ''}
|
|
${ip.ipv6? ip.ipv6 + '<br/>' : ''}
|
|
<br/>
|
|
${ip.mac}
|
|
${ip.vendor? '<br/>(' + ip.vendor + ')' : ''}
|
|
${ip.meta.values.length? '<br/>' + JSON.stringify(ip.meta.values) : ''}
|
|
</center>`;
|
|
break;
|
|
|
|
default:
|
|
nodeDiv.innerHTML = `<b>${node.id}</b>`
|
|
}
|
|
|
|
nodeDiv.style.color = typeColors[node.type];
|
|
nodeDiv.className = 'node-div';
|
|
|
|
const dataDiv = document.createElement('div');
|
|
|
|
dataDiv.id = 'datadiv_' + node.id;
|
|
dataDiv.className = 'node-data-div';
|
|
dataDiv.style.visibility = 'hidden';
|
|
dataDiv.style.display = 'none';
|
|
|
|
dataDiv.innerHTML = '<br/><pre>' + node.type + ' ' + JSON.stringify(node.entity, null, 2) + '</pre>';
|
|
|
|
nodeDiv.appendChild(dataDiv);
|
|
|
|
return new THREE.CSS2DObject(nodeDiv);
|
|
}
|
|
</script>
|
|
|
|
<script id="graph">
|
|
|
|
const Graph = ForceGraph3D({
|
|
extraRenderers: [new THREE.CSS2DRenderer()],
|
|
controlType: 'orbit'
|
|
})
|
|
(document.getElementById('3d-graph'))
|
|
.jsonUrl('bettergraph.json')
|
|
.nodeLabel('id')
|
|
.nodeColor(node => typeNodeColors[node.type])
|
|
.linkDirectionalArrowLength(3.5)
|
|
.linkDirectionalArrowRelPos(1)
|
|
/*
|
|
.linkThreeObjectExtend(true)
|
|
.linkThreeObject(link => {
|
|
const sprite = new SpriteText(link.edge.type);
|
|
sprite.color = 'lightgrey';
|
|
sprite.textHeight = 1.5;
|
|
return sprite;
|
|
})
|
|
.linkPositionUpdate((sprite, {start, end}) => {
|
|
const middlePos = Object.assign(...['x', 'y', 'z'].map(c => ({
|
|
[c]: start[c] + (end[c] - start[c]) / 2 // calc middle point
|
|
})));
|
|
Object.assign(sprite.position, middlePos);
|
|
})
|
|
*/
|
|
.nodeThreeObject(renderNodeHTML)
|
|
.nodeThreeObjectExtend(true)
|
|
.onNodeClick(node => {
|
|
if( targetNode != null ) {
|
|
const prev = document.getElementById('datadiv_' + targetNode.id);
|
|
prev.style.visibility = 'hidden';
|
|
prev.style.display = 'none';
|
|
}
|
|
targetNode = node;
|
|
|
|
const curr = document.getElementById('datadiv_' + targetNode.id);
|
|
curr.style.visibility = 'visible';
|
|
curr.style.display = 'block';
|
|
|
|
// Aim at node from outside it
|
|
const distance = 40;
|
|
const distRatio = 1 + distance/Math.hypot(node.x, node.y, node.z);
|
|
|
|
Graph.cameraPosition(
|
|
{ x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio }, // new position
|
|
node, // lookAt ({ x, y, z })
|
|
3000 // ms transition duration
|
|
);
|
|
});
|
|
</script>
|
|
</body> |