Permalink
Browse files

Major: Add HTTP Server for serving content (fixes #2)

restructure files
Rework Authentication core.
  • Loading branch information...
1 parent 97d470f commit a85b01aa7481af11bc6f7036babe69d792147f86 @mhils committed Jul 2, 2012
Showing with 211 additions and 105 deletions.
  1. BIN docs/hnp-popout.png
  2. 0 gui/{static → }/css/helper.less
  3. 0 gui/{static → }/css/preview.less
  4. 0 gui/{static → }/css/style.css
  5. 0 gui/{static → }/css/style.less
  6. BIN gui/{static → }/images/header.jpg
  7. BIN gui/{static → }/images/resourceCSSIcon.png
  8. BIN gui/{static → }/images/resourceDocumentIcon.png
  9. BIN gui/{static → }/images/resourceJSIcon.png
  10. BIN gui/{static → }/images/resourcePlainIcon.png
  11. BIN gui/{static → }/images/window-new.png
  12. +8 −6 gui/{static → }/index.html
  13. 0 gui/{static → }/js/backbone.js
  14. 0 gui/{static → }/js/backbone.marionette.js
  15. 0 gui/{static → }/js/closure-library
  16. +16 −0 gui/js/honeyproxy/Config.js
  17. 0 gui/{static → }/js/honeyproxy/DetailView.js
  18. +29 −12 gui/{static → }/js/honeyproxy/Flow.js
  19. 0 gui/{static → }/js/honeyproxy/FlowView.js
  20. 0 gui/{static → }/js/honeyproxy/MainLayout.js
  21. 0 gui/{static → }/js/honeyproxy/TableSorter.js
  22. 0 gui/{static → }/js/honeyproxy/Traffic.js
  23. 0 gui/{static → }/js/honeyproxy/TrafficView.js
  24. +2 −0 gui/{static → }/js/honeyproxy/flows/CssFlow.js
  25. +19 −0 gui/js/honeyproxy/flows/DocumentFlow.js
  26. +18 −0 gui/js/honeyproxy/flows/ImageFlow.js
  27. 0 gui/{static → }/js/honeyproxy/honeyproxy.js
  28. 0 gui/{static/js/honeyproxy/PopOut.js → js/honeyproxy/popOut.js}
  29. 0 gui/{static → }/js/honeyproxy/utilities.js
  30. +2 −6 gui/{static → }/js/honeyproxy/websocket.js
  31. 0 gui/{static → }/js/jquery-1.7.2.js
  32. BIN gui/{static → }/js/jquery-ui/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png
  33. BIN gui/{static → }/js/jquery-ui/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png
  34. BIN gui/{static → }/js/jquery-ui/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png
  35. BIN gui/{static → }/js/jquery-ui/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png
  36. BIN gui/{static → }/js/jquery-ui/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png
  37. BIN gui/{static → }/js/jquery-ui/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png
  38. BIN gui/{static → }/js/jquery-ui/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png
  39. BIN gui/{static → }/js/jquery-ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png
  40. BIN gui/{static → }/js/jquery-ui/css/smoothness/images/ui-icons_222222_256x240.png
  41. BIN gui/{static → }/js/jquery-ui/css/smoothness/images/ui-icons_2e83ff_256x240.png
  42. BIN gui/{static → }/js/jquery-ui/css/smoothness/images/ui-icons_454545_256x240.png
  43. BIN gui/{static → }/js/jquery-ui/css/smoothness/images/ui-icons_888888_256x240.png
  44. BIN gui/{static → }/js/jquery-ui/css/smoothness/images/ui-icons_cd0a0a_256x240.png
  45. 0 gui/{static → }/js/jquery-ui/css/smoothness/jquery-ui.css
  46. 0 gui/{static → }/js/jquery-ui/jquery-ui.min.js
  47. 0 gui/{static → }/js/jquery.splitter.js
  48. 0 gui/{static → }/js/prettyprint.js
  49. 0 gui/{static → }/js/underscore.js
  50. +0 −13 gui/static/js/honeyproxy/flows/DocumentFlow.js
  51. +0 −18 gui/static/js/honeyproxy/flows/ImageFlow.js
  52. +20 −12 honeyproxy.py
  53. +51 −0 libhproxy/content.py
  54. +24 −27 libhproxy/flowcollection.py
  55. 0 { → libhproxy}/gui/__init__.py
  56. +8 −9 { → libhproxy}/gui/session.py
  57. +14 −2 libhproxy/honey.py
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
@@ -67,10 +67,12 @@
<td class=contenttype>
<%- getContentType() %></td>
<td class=size>
- <div title="<%- getRawContentSize() %> Bytes">
- <%- getContentSize() %>
+ <div class=hidden><%- /*table sorter */ getResponseContentLength() %></div>
+ <div title="<%- getResponseContentLength() %> Bytes">
+ <%- getResponseContentLengthFormatted() %>
</div></td>
<td class=time>
+ <div class=hidden><%- /*table sorter */ get("request").timestamp %></div>
<div class=timestamp title="Timestamp: <%- get("request").timestamp %>">
<%- getDate().toLocaleTimeString() %>, <%- ("0"+getDate().getDay()).slice(-2)+"."+("0"+getDate().getMonth()).slice(-2) %>.<br>
</div>
@@ -84,9 +86,8 @@
<div title="Pop out" class=popoutbutton></div>
<div class=content>
<h2 class=title><%- getFilename() %></h2>
- <% if(hasResponseContent()) print(getPreview());
- else print("No response content.");
- %>
+ <% if(hasResponseContent()) return getPreview();
+ else print("No response content."); %>
</div>
</script>
<script id=template-header type="text/html">
@@ -150,6 +151,7 @@ <h2 class=title><%- getFilename() %></h2>
<script src="./js/backbone.marionette.js"></script>
<script src="./js/honeyproxy/honeyproxy.js"></script>
+<script src="./js/honeyproxy/Config.js"></script>
<script src="./js/honeyproxy/utilities.js"></script>
<script src="./js/honeyproxy/websocket.js"></script>
<script src="./js/honeyproxy/Flow.js"></script>
@@ -162,6 +164,6 @@ <h2 class=title><%- getFilename() %></h2>
<script src="./js/honeyproxy/TableSorter.js"></script>
<script src="./js/honeyproxy/MainLayout.js"></script>
<script src="./js/honeyproxy/DetailView.js"></script>
-<script src="./js/honeyproxy/PopOut.js"></script>
+<script src="./js/honeyproxy/popOut.js"></script>
</body>
</html>
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,16 @@
+(function(){
+
+
+ Config = function(data){
+ this.storage = {}; //localStorage might leak sensitive information
+ $.extend(this.storage, data);
+ };
+ Config.prototype.get = function(id){
+ return this.storage[id];
+ };
+ Config.prototype.set = function(id,val){
+ this.storage[id] = val;
+ }
+ HoneyProxy.config = new Config(
+ JSON.parse(decodeURIComponent(location.hash).replace("#","")));
+})();
@@ -22,28 +22,45 @@ HoneyProxy.Flow = Backbone.Model.extend({
getStatusCode: function(){
return this.get("response").code;
},
- getContent: function(){
- return this.get("response").content;
- },
- getRawContentSize: function(){
- return this.get("response").content.length
- },
- getContentSize: function(){
- if(!this.has("contentSize"))
+ getResponseContentURL: function(action){
+ var url = HoneyProxy.config.get("content")
+ +"/"+this.get("id")
+ +"/response/"+action
+ +"?"+$.param(
+ {"auth":HoneyProxy.config.get("auth")});
+ return url;
+ },
+ getResponseContentDownloadURL: function(){
+ return this.getResponseContentURL("attachment");
+ },
+ getResponseContentViewURL: function(){
+ return this.getResponseContentURL("inline");
+ },
+ getResponseContent: function(callback){
+ if(this.hasResponseContent())
+ return $.get(this.getResponseContentViewURL(),callback);
+ else
+ return callback("");
+ },
+ getResponseContentLength: function(){
+ return this.get("response").contentLength
+ },
+ getResponseContentLengthFormatted: function(){
+ if(!this.has("responseContentLength"))
{
var prefix = ["B","KB","MB","GB"];
- var size = this.getRawContentSize();;
+ var size = this.getResponseContentLength();
while(size > 1024 && prefix.length > 1){
prefix.shift();
size = size / 1024;
}
- this.set("contentSize",Math.floor(size)+prefix.shift());
+ this.set("responseContentLength",Math.floor(size)+prefix.shift());
}
- return this.get("contentSize");
+ return this.get("responseContentLength");
},
hasResponseContent: function(){
- return this.getRawContentSize() > 0;
+ return this.getResponseContentLength() > 0;
},
getContentType: function(){
if(!this.has("contentType"))
@@ -5,6 +5,8 @@ HoneyProxy.CSSFlow = HoneyProxy.DocumentFlow.extend({
}, {matches: function(data){
if(data.contentType)
return !!data.contentType.match(/css/i);
+ else if(data.path)
+ return !!data.path.match(/\.css$/i);
return false;
}});
HoneyProxy.flowModels.unshift(HoneyProxy.CSSFlow);
@@ -0,0 +1,19 @@
+HoneyProxy.DocumentFlow = HoneyProxy.Flow.extend({
+ getCategory: function(){
+ return "document";
+ },
+ getPreview: function(){
+ var $pre = $("<pre>");
+ $pre.text("Loading...");
+ this.getResponseContent(function(data){
+ console.log(arguments)
+ $pre.text("Content: \n"+data);
+ });
+ return $pre[0];
+ }
+}, {matches: function(data){
+ if(data.contentType)
+ return !!data.contentType.match(/application|text/i);
+ return false;
+}});
+HoneyProxy.flowModels.unshift(HoneyProxy.DocumentFlow);
@@ -0,0 +1,18 @@
+HoneyProxy.ImageFlow = HoneyProxy.Flow.extend({
+ getCategory: function(){
+ return "image";
+ },
+ getPreview: function(){
+ //var contentType = this.getContentType() || "image/"+this.getFilename().split(".").pop();
+ //contentType = contentType.replace(/[^a-zA-Z0-9\/]/g,"");
+ return '<img src="'+this.getResponseContentViewURL()+'" alt="preview" >';
+ }
+
+}, {matches: function(data){
+ if(data.contentType)
+ return !!data.contentType.match(/image/i);
+ else if(data.path)
+ return !!data.path.match(/\.(gif|png|jpg|jpeg)$/i);
+ return false;
+}});
+HoneyProxy.flowModels.unshift(HoneyProxy.ImageFlow);
@@ -22,10 +22,9 @@ HoneyProxy.websocket = {
this.ws.send(JSON.stringify(jsonMsg));
},
initialize: function(){
- var conn = this.getConnectionData();
- this.ws = new WebSocket(conn.ws);
+ this.ws = new WebSocket(HoneyProxy.config.get("ws"));
this.ws.onopen = function(e){
- HoneyProxy.websocket.send({action:"auth",key:conn.auth});
+ HoneyProxy.websocket.send({action:"auth",key:HoneyProxy.config.get("auth")});
HoneyProxy.log("Connection etablished");
};
this.ws.onmessage = this.onmessage;
@@ -51,8 +50,5 @@ HoneyProxy.websocket = {
}
HoneyProxy.log(e);
- },
- getConnectionData: function(){
- return JSON.parse(decodeURIComponent(location.hash).replace("#",""));
}
};
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,13 +0,0 @@
-HoneyProxy.DocumentFlow = HoneyProxy.Flow.extend({
- getCategory: function(){
- return "document";
- },
- getPreview: function(){
- return "<pre>Content: "+_.escape(this.getContent())+" </pre>";
- }
-}, {matches: function(data){
- if(data.contentType)
- return !!data.contentType.match(/text/i);
- return false;
-}});
-HoneyProxy.flowModels.unshift(HoneyProxy.DocumentFlow);
@@ -1,18 +0,0 @@
-HoneyProxy.ImageFlow = HoneyProxy.Flow.extend({
- getCategory: function(){
- return "image";
- },
- getPreview: function(){
- var contentType = this.getContentType() || "image/"+this.getFilename().split(".").pop();
- contentType = contentType.replace(/[^a-zA-Z0-9\/]/g,"");
- return '<img alt="preview" src="data:'+contentType+';base64,'+_.escape(window.btoa(this.getContent()))+'">';
- }
-
-}, {matches: function(data){
- if(data.contentType)
- return !!data.contentType.match(/image/i);
- else if(data.path)
- return !!data.path.match(/\.(gif|png|jpg|jpeg)$/i);
- return false;
-}});
-HoneyProxy.flowModels.unshift(HoneyProxy.ImageFlow);
View
@@ -17,7 +17,7 @@
-import gui.session
+import libhproxy.gui.session as session
import sys, json, urllib
sys.path.append("./mitmproxy") #git submodules "hack"
@@ -28,14 +28,13 @@
from twisted.web.server import Site
from twisted.web.static import File
from twisted.internet import reactor, task
+from twisted.web.resource import Resource
-from libhproxy.websockets import WebSocketsResource
-from libhproxy import proxy as hproxy, cmdline as hcmdline, version
+#from libhproxy.websockets import WebSocketsResource
+from libhproxy import proxy as hproxy, cmdline as hcmdline, version, content
from libhproxy.honey import HoneyProxy
-from autobahn.websocket import WebSocketServerFactory, \
- WebSocketServerProtocol, \
- listenWS
+from autobahn.websocket import listenWS
def main():
@@ -69,26 +68,34 @@ def main():
print >> sys.stderr, "%(name)s:" % version.NAME, v.args[0]
sys.exit(1)
+ HoneyProxy.setAuthKey(options.apiauth)
+
#set up HoneyProxy GUI
- guiSessionFactory = gui.session.GuiSessionFactory("ws://localhost:"+str(options.apiport),options.apiauth)
+ guiSessionFactory = session.GuiSessionFactory("ws://localhost:"+str(options.apiport))
+ #WebSocket
listenWS(guiSessionFactory)
-
#websocketRes = WebSocketsResource(guiSessionFactory)
#reactor.listenTCP(options.apiport, Site(websocketRes))
- reactor.listenTCP(options.guiport, Site(File("./gui/static")))
+ root = Resource()
+ root.putChild("app",File("./gui"))
+ root.putChild("files", content.ContentAPI())
+ reactor.listenTCP(options.guiport, Site(root))
+
#HoneyProxy Master
p = hproxy.HoneyProxyMaster(server, dumpoptions, filt, guiSessionFactory)
HoneyProxy.setProxyMaster(p)
p.start()
wsURL = "ws://localhost:"+str(options.apiport)
+ contentURL = "http://localhost:"+str(options.guiport)+"/files"
urlData = urllib.quote(json.dumps({
"ws": wsURL,
- "auth": guiSessionFactory.authKey
+ "auth": HoneyProxy.getAuthKey(),
+ "content": contentURL
}))
- guiURL = "http://localhost:"+str(options.guiport)+"/#"+urlData
+ guiURL = "http://localhost:"+str(options.guiport)+"/app#"+urlData
if not options.nogui:
#start gui
@@ -97,7 +104,8 @@ def main():
else:
print "GUI: "+guiURL
print "WebSocket API URL: "+wsURL
- print "Auth key: "+ guiSessionFactory.authKey
+ print "HTTP Content API Root: "+contentURL
+ print "Auth key: "+ HoneyProxy.getAuthKey()
#run!
l = task.LoopingCall(p.tick)
View
@@ -0,0 +1,51 @@
+from twisted.web.resource import Resource
+from libhproxy.honey import HoneyProxy
+#serve request content via HTTP
+class ContentAPI(Resource):
+ isLeaf = True
+ def render_GET(self, request):
+ if self.isAuthenticated(request):
+ try:
+ if(len(request.postpath) != 3):
+ raise Exception("invalid parameter length")
+ flow = HoneyProxy.getProxyMaster().getFlowCollection().getFlow(int(request.postpath[0]))
+ isResponse = request.postpath[1] == "response"
+
+ obj = getattr(flow,request.postpath[1])
+
+ isView = request.postpath[2] == "inline"
+ if (isResponse):
+ #add important headers from original request
+ headers = ["Content-Type","Content-Encoding","Transfer-Encoding"]
+ for h in headers:
+ if(h in obj.headers):
+ request.setHeader(h,obj.headers.get(h)[0])
+
+ #this would fail on 301 redirects
+ #fix responsecode
+ #request.setResponseCode(obj.code)
+
+ #fix content disposition for attachment download
+ cdisp = obj.headers.get("Content-Disposition")
+ if(cdisp == None):
+ #do minimal file name guessing
+ cdisp = 'inline; filename="'+flow.request.path.split("?")[0].split("/")[-1]+'"'
+ if isView:
+ request.setHeader("Content-Disposition",cdisp.replace("attachment", "inline"))
+ else:
+ request.setHeader("Content-Disposition",cdisp.replace("inline", "attachment"))
+
+ return obj.content
+ except Exception as e:
+ print e
+ return "<html><body>Invalid request.</body></html>"
+ else:
+ request.setResponseCode(402)
+ return "<html><body>Please authenticate.</body></html>"
+
+
+ def isAuthenticated(self,request):
+ try:
+ return request.args["auth"][0] == HoneyProxy.getAuthKey()
+ except:
+ return False
Oops, something went wrong. Retry.

0 comments on commit a85b01a

Please sign in to comment.