|
22 | 22 |
|
23 | 23 | import jinja2
|
24 | 24 |
|
| 25 | +# |
| 26 | +# Example of syntax (from https://bramp.github.io/js-sequence-diagrams/): |
| 27 | +# |
| 28 | +# Title: Here is a title |
| 29 | +# A->B: Normal line |
| 30 | +# B-->C: Dashed line |
| 31 | +# C->>D: Open arrow |
| 32 | +# D-->>A: Dashed open arrow |
| 33 | +# # Example of a comment. |
| 34 | +# Note left of A: Note to the\n left of A |
| 35 | +# Note right of A: Note to the\n right of A |
| 36 | +# Note over A: Note over A |
| 37 | +# Note over A,B: Note over both A and B |
| 38 | +# |
| 39 | + |
25 | 40 | JINJA_TEMPLATE = """
|
26 | 41 | <!doctype html>
|
27 | 42 | <html>
|
28 | 43 | <head>
|
29 | 44 | <link rel="stylesheet" type="text/css" href=""https://bramp.github.io/js-sequence-diagrams/css/sequence-diagram-min.css" media="screen" />
|
| 45 | +<style> |
| 46 | + .signal text { |
| 47 | + fill: #000000; |
| 48 | +} |
| 49 | +.signal text:hover { |
| 50 | + fill: #aaaaaa |
| 51 | +} |
| 52 | +.note rect, .note path { |
| 53 | + fill: #ffff80; |
| 54 | +} |
| 55 | +.title rect, .title path{ |
| 56 | + fill: #ff80ff; |
| 57 | +} |
| 58 | +.actor rect, .actor path { |
| 59 | + fill: #80ffff |
| 60 | +} |
| 61 | +
|
| 62 | +</style> |
30 | 63 | </head>
|
31 | 64 | <body onload='showseq();'>
|
32 | 65 | <p>Sequence diagram from emlclient log {{ logfilename }} - wait a few seconds for the diagram to appear...</p>
|
@@ -145,27 +178,34 @@ def addheaders(events,msgheaders,headerstoshow,direction):
|
145 | 178 | events.append( f"Note {direction}: {text}" )
|
146 | 179 | return
|
147 | 180 |
|
148 |
| -# parse a message from the logfile into a reqat/response tuple |
| 181 | +# parse a message from the logfile into a request/response tuple |
149 | 182 | def decodemessage(msg):
|
150 | 183 | request = {'intent':None,'method': None, 'url': None, 'headers':[], 'body':None}
|
151 |
| - response = {'status': None,'headers':[], 'body':None} |
152 |
| - parts = re.search( r"^(?:INTENT: (.*?)\n\n)?(GET|PUT|POST|HEAD|DELETE|POST) +(\S+)\n( .*?)\n\n(?::+?=\n(.*?)\n-+?=\n)?.*?\n\nResponse: (\d+)\n( .*?)\n\n(?::+?@\n(.*?)\n-+?@\n)?",msg, flags=re.DOTALL+re.MULTILINE ) |
| 184 | + response = {'status': None,'headers':[], 'body':None, 'action':None } |
| 185 | + parts = re.search( r"\n*(?:INTENT: ([^\n]*?)\n+)?(?:(?:(GET|PUT|POST|HEAD|DELETE|POST) +(\S+)\n((?: +.*?\n)+)\n*)(?::+?=\n(.*?)\n-+?=\n)?.*?\n+Response: (\d+?)\n( .*?)\n\n(?::+?@\n(.*?)\n-+?@\n+)?)?(?:ACTION: (.*?)\n)?",msg, flags=re.DOTALL ) |
| 186 | + if parts.group(0) == '': |
| 187 | + burp |
| 188 | +# for l,g in enumerate(parts.groups()): |
| 189 | +# print( f"{l=} {g=}" ) |
153 | 190 | request['intent'] = parts.group(1)
|
154 | 191 | request['method'] = parts.group(2)
|
155 | 192 | request['url'] = parts.group(3)
|
156 |
| - for hdrline in parts.group(4).split( "\n" ): |
157 |
| - hsplit = hdrline.strip().split(": ",1) |
158 |
| - hsplit.append("") |
159 |
| - hdr, value = hsplit[0],hsplit[1] |
160 |
| - request['headers'].append( (hdr,value) ) |
| 193 | + if parts.group(4) is not None: |
| 194 | + for hdrline in parts.group(4).split( "\n" ): |
| 195 | + hsplit = hdrline.strip().split(": ",1) |
| 196 | + hsplit.append("") |
| 197 | + hdr, value = hsplit[0],hsplit[1] |
| 198 | + request['headers'].append( (hdr,value) ) |
161 | 199 | request['body'] = parts.group(5).strip() if parts.group(5) else None
|
162 | 200 | response['status'] = parts.group(6)
|
163 |
| - for hdrline in parts.group(7).split( "\n" ): |
164 |
| - hsplit = hdrline.strip().split(": ",1) |
165 |
| - hsplit.append("") |
166 |
| - hdr, value = hsplit[0],hsplit[1] |
167 |
| - response['headers'].append( (hdr,value) ) |
| 201 | + if parts.group(7) is not None: |
| 202 | + for hdrline in parts.group(7).split( "\n" ): |
| 203 | + hsplit = hdrline.strip().split(": ",1) |
| 204 | + hsplit.append("") |
| 205 | + hdr, value = hsplit[0],hsplit[1] |
| 206 | + response['headers'].append( (hdr,value) ) |
168 | 207 | response['body'] = parts.group(8).strip() if parts.group(8) else None
|
| 208 | + response['action'] = parts.group(9) |
169 | 209 | return (request,response)
|
170 | 210 |
|
171 | 211 | def findheader(hdrname,requestorresponse,notfoundreturn=None):
|
@@ -257,44 +297,51 @@ def main():
|
257 | 297 |
|
258 | 298 | for i,msg in enumerate(msgs):
|
259 | 299 | request,response = decodemessage(msg)
|
260 |
| - # if INTENT: then add a note for it |
261 |
| - |
| 300 | +# print( f"{request=}" ) |
| 301 | +# print( f"{response=}" ) |
262 | 302 | line = i
|
263 |
| - |
264 |
| - reqparts = urllib.parse.urlparse(request['url']) |
265 | 303 |
|
266 |
| - path = reqparts.path |
267 |
| - if reqparts.query: |
268 |
| - path += "?"+reqparts.query |
| 304 | + if request['method']: |
269 | 305 |
|
270 |
| - if request['intent']: |
271 |
| - events.append( f"Note left of me: INTENT: {request['intent']}" ) |
| 306 | + reqparts = urllib.parse.urlparse(request['url']) |
272 | 307 |
|
273 |
| - events.append( f"me->{host}: {request['method']} {sanitise(path,wrap=True)}" ) |
274 |
| - |
275 |
| - addheaders(events,request['headers'],reqshowheaders,"left of me" ) |
276 |
| - |
277 |
| - if request['body']: |
278 |
| - if contentshouldbeshown(request): |
279 |
| - reqbody = sanitise(request['body'], wrap=True) |
280 |
| - else: |
281 |
| - reqbody = sanitise(request['body'], wrap=False, maxlines=5) |
282 |
| - |
283 |
| - if reqbody: |
284 |
| - events.append( f"Note left of me: {reqbody}" ) |
| 308 | + path = reqparts.path |
| 309 | + if reqparts.query: |
| 310 | + path += "?"+reqparts.query |
| 311 | + |
| 312 | + # if INTENT: then add a note for it |
| 313 | + if request['intent']: |
| 314 | + events.append( f"Note left of me: INTENT: {request['intent']}" ) |
285 | 315 |
|
286 |
| - events.append( f"{host}-->me: {response['status']}" ) |
287 |
| - |
288 |
| - addheaders(events,response['headers'],respshowheaders,f"right of {host}" ) |
289 |
| - |
290 |
| - if response['body']: |
291 |
| - if contentshouldbeshown(response): |
292 |
| - respbody = sanitise(response['body'], wrap=False) |
293 |
| - else: |
294 |
| - respbody = sanitise(response['body'], wrap=False, maxlines=5) |
295 |
| -# respbody = "Content type not shown..." |
296 |
| - |
297 |
| - events.append( f"Note right of {host}: {respbody}" ) |
| 316 | + events.append( f"me->{host}: {request['method']} {sanitise(path,wrap=True)}" ) |
| 317 | + |
| 318 | + addheaders(events,request['headers'],reqshowheaders,"left of me" ) |
| 319 | + |
| 320 | + if request['body']: |
| 321 | + if contentshouldbeshown(request): |
| 322 | + reqbody = sanitise(request['body'], wrap=True) |
| 323 | + else: |
| 324 | + reqbody = sanitise(request['body'], wrap=False, maxlines=5) |
| 325 | + |
| 326 | + if reqbody: |
| 327 | + events.append( f"Note left of me: {reqbody}" ) |
| 328 | + |
| 329 | + events.append( f"{host}-->me: {response['status']}" ) |
| 330 | + |
| 331 | + addheaders(events,response['headers'],respshowheaders,f"right of {host}" ) |
| 332 | + |
| 333 | + if response['body']: |
| 334 | + if contentshouldbeshown(response): |
| 335 | + respbody = sanitise(response['body'], wrap=False) |
| 336 | + else: |
| 337 | + respbody = sanitise(response['body'], wrap=False, maxlines=5) |
| 338 | + # respbody = "Content type not shown..." |
| 339 | + |
| 340 | + events.append( f"Note right of {host}: {respbody}" ) |
| 341 | + |
| 342 | + # if ACTION: then add a note for it |
| 343 | + if response['action']: |
| 344 | + events.append( f"Note over me: {response['action']}" ) |
298 | 345 |
|
299 | 346 | j2_template = jinja2.Template(JINJA_TEMPLATE)
|
300 | 347 | data = {
|
|
0 commit comments