Skip to content

Commit

Permalink
Merge pull request fw42#4 from fw42/coffeescript_rewrite
Browse files Browse the repository at this point in the history
CoffeeScript rewrite
  • Loading branch information
fw42 committed May 27, 2013
2 parents 6296364 + 782cd59 commit 945d22d
Show file tree
Hide file tree
Showing 57 changed files with 560 additions and 831 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
other
*.swp
README.html
30 changes: 20 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
YUICOMPRESSOR ?= /path/to/YUICOMPRESSOR
all: hpfeeds

all: hm.js
hpfeeds: hpfeeds_client hpfeeds_server

hm.js: all.js
java -jar $(YUICOMPRESSOR) -o static/hm.js all.js
rm all.js
random: basic_client random_server

all.js:
cat static/extern/jquery-*.js static/main.js static/layout.js static/extern/bootstrap.js static/feed_socketio_hpfeeds.js static/map.js > all.js
udp: basic_client udp_server

clean:
rm -f static/hm.js
hpfeeds_client: client/coffee/* client/coffee/feeds/hpfeeds.coffee
coffee -cj client/js/honeymap.js client/coffee/*.coffee client/coffee/feeds/hpfeeds.coffee

hpfeeds_server: server/coffee/* server/coffee/honeymap/hpfeeds.coffee
coffee -cj server/js/honeymap.js server/coffee/config.coffee server/coffee/http.coffee server/coffee/socketio.coffee server/coffee/honeymap/hpfeeds.coffee server/coffee/main.coffee

basic_client: client/coffee/* client/coffee/feeds/basic.coffee
coffee -cj client/js/honeymap.js client/coffee/*.coffee client/coffee/feeds/basic.coffee

.PHONY: all clean
random_server: server/coffee/* server/coffee/honeymap/random.coffee
coffee -cj server/js/honeymap.js server/coffee/config.coffee server/coffee/http.coffee server/coffee/socketio.coffee server/coffee/honeymap/random.coffee server/coffee/main.coffee

udp_server: server/coffee/* server/coffee/honeymap/udp.coffee
coffee -cj server/js/honeymap.js server/coffee/config.coffee server/coffee/http.coffee server/coffee/socketio.coffee server/coffee/honeymap/udp.coffee server/coffee/main.coffee

clean:
rm client/js/honeymap.js
rm server/js/honeymap.js
29 changes: 21 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ HoneyMap
========

HoneyMap is a web application which visualizes a live stream of
GPS locations on a SVG world map.
In principle, it can be used with any stream of GPS data. For our application,
we use honeypot captures, provided by several [hpfeeds](https://github.com/rep/hpfeeds)
from the [Honeynet Project](http://www.honeynet.org/).
GPS locations on a SVG world map. In principle, it can be used
with any stream of GPS data. For our application, we use honeypot
captures, provided by several [hpfeeds](https://github.com/rep/hpfeeds)
from the [Honeynet Project](http://www.honeynet.org/). For more information
on our instance of HoneyMap, see
[HoneyMap - Visualizing Worldwide Attacks in Real-Time](http://www.honeynet.org/node/960).

It makes use of [jQuery](http://jquery.com/), [node.js](http://nodejs.org/),
[socket.io](http://socket.io/) (HTML5 websockets), [jVectorMap](http://jvectormap.com/) and
[jQuery Transit](http://ricostacruz.com/jquery.transit/) (CSS3 animations).
It is written in [CoffeeScript](http://coffeescript.org/) and makes use of
[jQuery](http://jquery.com/), [node.js](http://nodejs.org/),
[socket.io](http://socket.io/) (HTML5 websockets), [jVectorMap](http://jvectormap.com/)
and [jQuery Transit](http://ricostacruz.com/jquery.transit/) (CSS3 animations).

Tested with node.js v0.8.9 and socket.io v0.9.10.
Tested with node.js v0.10.7 and socket.io v0.9.14.

Example
-------
Expand All @@ -36,3 +39,13 @@ Authors
Forks
-----
* [German Telecom (DTAG) Sicherheitstacho](http://www.sicherheitstacho.eu/)

Setup
-----
* Install node.js (tested with [0.10.7](http://nodejs.org/dist/v0.10.7/node-v0.10.7.tar.gz))
* In ```server/```
* ```npm install```
* In ```server/coffee/```, copy ```config.coffee_example``` to ```config.coffee``` and fill in
your [hpfriends](http://hpfriends.honeycloud.net) credentials.
* ```make```
* ```server/honeymap.sh```
2 changes: 2 additions & 0 deletions client/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
index.html
js/honeymap.js
14 changes: 14 additions & 0 deletions client/coffee/feeds/basic.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Feed
constructor: (map, log, instance) ->
@map = map
@log = log
transport = new Transport(instance, @handler)

handler: (data) =>
lat = data.lat
lng = data.lng
eventName = data.type
marker = new Marker(@map, lat, lng, eventName)
return unless marker.regionCode
@map.addMarker(marker)
@log.add "New event in " + marker.regionName() + " " + marker.name()
38 changes: 38 additions & 0 deletions client/coffee/feeds/hpfeeds.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
class Feed
constructor: (map, log, instance) ->
@map = map
@log = log
transport = new Transport(instance, @handler)

handler: (data) =>
lat1 = data.latitude
lng1 = data.longitude
return unless lat1 and lng1
src = new Marker(@map, lat1, lng1, data.type, "src", data.countrycode, data.city)
return if src.x == 0 and src.y == 0
lat2 = data.latitude2
lng2 = data.longitude2
if lat2 and lng2
dst = new Marker(@map, lat2, lng2, data.type, "dst", data.countrycode2, data.city2)
if dst.x == 0 and dst.y == 0 then dst = null
@addLog(src, dst, data.md5)
@map.addMarker(src)
@map.addMarker(dst) if dst

addLog: (src, dst, md5) ->
return unless src.regionName()
timestamp = new Date().toTimeString().substring(0,8)
attacktype = if src.eventName == "thug.events" then "scan" else "attack"
logstr = """
<div class="log_timestamp">#{timestamp}</div>
<div class="log_bracket">&lt;</div>#{src.eventName}<div class="log_bracket">&gt;</div>
New #{attacktype} from <div class="log_country">#{src.location()}</div>
<small>#{src.name()}</small>
"""
if dst and dst.regionName()
logstr += " to <div class=\"log_country\">#{dst.location()}</div> <small>#{dst.name()}</small>"
if md5
logstr += " <div class=\"log_bracket2\">[</div>" + \
"<div class=\"log_info\"><a href=\"http://www.virustotal.com/search/?query=#{md5}\">#{md5.substr(0,9)}</a></div>" + \
"<div class=\"log_bracket2\">]</div>"
@log.add logstr
14 changes: 14 additions & 0 deletions client/coffee/feeds/random_local.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Feed
constructor: (map, log, pause) ->
@map = map
@log = log
window.setInterval((=> window.setTimeout(@handler, Math.random() * pause)), 2 * pause)

handler: =>
while true
lat = Math.random() * 180 - 90
lng = Math.random() * 360 - 180
marker = new Marker(@map, lat, lng, "random_local")
break if marker.regionCode
@map.addMarker(marker)
@log.add "New event in " + marker.regionName() + " " + marker.name()
110 changes: 110 additions & 0 deletions client/coffee/honeymap.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
###
This file is part of HoneyMap (https://github.com/fw42/honeymap/),
developed within the Honeynet Project (https://www.honeynet.org/),
written by Florian Weingarten, Mark Schloesser, Johannes Gilger.
See website for license and contact information.
###

class Honeymap
constructor: (config) ->
@config = config
@hits = { region: { total: {} }, marker: { total: 0 } }
@captions = {}
@mapElem = jQuery('#world-map')
@fitSize()
@mapElem.vectorMap(
backgroundColor: ''
markerStyle:
initial:
fill: config.colors.src.fill
stroke: config.colors.src.stroke
r: 3
series:
markers: []
regions: [
scale: config.colors.scale
attribute: 'fill'
normalizeFunction: 'linear'
values: {}
]
onRegionLabelShow: (ev, label, code) =>
label.html("<big>" + label.html() + "</big>")
label.append(Honeymap.eventCountSummary(@hits.region[code]))
onMarkerLabelShow: (ev, label, code) =>
label.html(@captions[code])
label.append(Honeymap.eventCountSummary(@hits.marker[code]))
)
@mapObj = @mapElem.vectorMap('get', 'mapObject')
@mapObj.regions['US'].config.name = "USA"

fitSize: ->
@mapElem.width(jQuery(document).width() - 100)
@mapElem.height(0.8 * jQuery(document).height())

updateRegionColors: ->
# Force recomputation of min and max for correct color scaling
@mapObj.series.regions[0].params.min = null
@mapObj.series.regions[0].params.max = null
# Update data
@mapObj.series.regions[0].setValues(@hits.region["total"])

removeOldestMarker: ->
# only remove src markers
toremove = jQuery(@mapElem.find("svg g circle.jvectormap-marker[fill=" + @config.colors.src.fill + "]")[0])
par = toremove.parent()
id = toremove.attr('data-index')
delete(@captions[id])
@mapObj.removeMarkers([ id ])
# Remove parent node too (jVectorMap does not do this by itself)
par.remove()

regionCode: (x, y) ->
efp = jQuery(document.elementFromPoint(x + @mapElem.offset().left, y + @mapElem.offset().top))
if efp.is('path')
return efp.attr('data-code')
else if efp.is('circle') || (efp.is('div') && efp.hasClass('marker_animation'))
# This is pretty ugly. If we hit an existing marker, make it invisible,
# recursively look again and then make it visible again.
efp.hide()
rc = @regionCode(x, y)
efp.show()
return rc
else
return null

regionName: (regionCode) ->
@mapObj.getRegionName(regionCode)

incMarkerCount: (marker) ->
@hits.marker[marker.id()] ||= {}
@hits.marker[marker.id()][marker.eventName] ||= 0
@hits.marker[marker.id()][marker.eventName]++
# only count src markers which are within a valid region
return unless marker.type == 'src' and rc = marker.regionCode
@hits.region[rc] ||= {}
@hits.region[rc][marker.eventName] ||= 0
@hits.region[rc][marker.eventName]++
@hits.region["total"][rc] ||= 0
@hits.region["total"][rc]++

addMarker: (marker) ->
marker.animate()
@captions[marker.id()] = marker.caption()
@incMarkerCount(marker)
@updateRegionColors()
# only add new markers div's to jVectorMap which do not exist yet
return if @mapObj.markers[marker.id()]
@hits.marker["total"]++
if @hits.marker["total"] > config.markersMaxVisible then @removeOldestMarker()
@mapObj.addMarker(marker.id(), { latLng: marker.gps(), name: marker.name(), style: @config.colors[marker.type] }, [])

@eventCountSummary: (hits) ->
return unless hits?
total = 0
summary = "<hr/>"
for type, count of hits
count ||= 0
summary += "<b>#{type}</b>: #{count}<br/>"
total += count
summary + "<hr/><b>total</b>: " + total + " events"
23 changes: 23 additions & 0 deletions client/coffee/log.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
class Log
constructor: (config) ->
@elem = jQuery("#log")
@max = config.markersMaxVisible
@fitSize()

fitSize: ->
@elem.width(0.6 * jQuery(document).width())
@elem.css("margin-top", 0.03 * jQuery(document).height())
@elem.height(0.15 * jQuery(document).height())

clearOld: ->
entries = @elem.find("div.log_entry")
if entries.length >= @max
entries.slice(0, entries.length/2).remove()
@elem.find("br").nextUntil('div.log_entry', 'br').remove()

add: (msg) ->
@clearOld()
# only automatically scroll down if the user did not manually scroll up before
scroll = (@elem.scrollTop() + @elem.innerHeight() == @elem[0].scrollHeight)
@elem.append('<div class="log_entry">' + msg + '</div><br/>')
if scroll then @elem.scrollTop(@elem[0].scrollHeight)
21 changes: 21 additions & 0 deletions client/coffee/main.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
config =
markersMaxVisible: 150
colors:
src: { stroke: 'darkred', fill: 'red' }
dst: { stroke: '#383F47', fill: '#F8E23B' }
scale: [ '#FFFFFF', '#0071A4' ]

jQuery(document).ready ->
log = new Log(config)
honeymap = new Honeymap(config)

jQuery(window).resize ->
honeymap.fitSize()
log.fitSize()

log.add "<b>Welcome to HoneyMap. This is a BETA version! Bug reports welcome :-)</b>"
log.add "Note that this is not <b>all</b> honeypots of the Honeynet Project,"
log.add "only those who voluntarily publish their captures to hpfeeds!"
log.add ""

new Feed(honeymap, log, "geoloc.events")
35 changes: 35 additions & 0 deletions client/coffee/marker.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
class Marker
constructor: (map, lat, lng, eventName, type, regionCode, cityName) ->
@map = map
@lat = lat
@lng = lng
@type = type || "src"
@eventName = eventName || "other"
point = @map.mapObj.latLngToPoint(@lat, @lng)
@x = point.x
@y = point.y
@regionCode = regionCode || @map.regionCode(@x, @y)
@cityName = cityName

animate: ->
cssClass = if @type == 'dst' then 'markerdst' else 'markersrc'
@map.mapElem.append(
jQuery('<div class="marker_animation ' + cssClass + '"></div>')
.css('left', @x + 'px')
.css('top', @y + 'px')
.css({ opacity: 1, scale: 0 })
.transition({ opacity: 0, scale: 1 }, 1000, 'linear', ->
jQuery(this).remove()
)
)

caption: ->
caption = "<small>(" + @lat + ", " + @lng + ")</small><br/>"
caption += "<big>" + @cityName + "</big> (" + @regionCode + ")" if @cityName
return caption

id: -> @lat + "," + @lng
name: -> "(" + @lat.toFixed(2) + ", " + @lng.toFixed(2) + ")"
gps: -> [ @lat, @lng ]
regionName: -> @map.regionName(@regionCode) if @regionCode
location: -> (if @cityName then @cityName + ", " else "") + @regionName()
4 changes: 4 additions & 0 deletions client/coffee/transport.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class Transport
constructor: (instance, handler) ->
@socket = io.connect('/')
@socket.on(instance, handler)
4 changes: 2 additions & 2 deletions static/main.css → client/css/honeymap.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ a {
position: absolute;
left: 1%;
bottom: 1%;
max-width: 20%;
max-width: 15%;
}

#logo1 img, #logo2 img {
Expand All @@ -63,7 +63,7 @@ a {
position: absolute;
right: 1%;
bottom: 1%;
max-width: 20%;
max-width: 15%;
}

#helpbtn {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
Loading

0 comments on commit 945d22d

Please sign in to comment.