11/*
22 httpgraph.js
3- @phreakocious - 2017-2022
3+ @phreakocious - 2017-2025
44
55 Chrome extension to accompany the HTTP Graph plugin for Gephi
66 Collects minimal details from http and https request/response headers and POSTs them to a REST API as you browse
77 I'm not a JS programmer
88*/
99
10- var scrub_parameters
11- var url_backend
12- const default_rest_port = "65444"
13- const default_scrub_parameters = false
10+ const default_rest_port = "65444" ;
11+ const default_scrub_parameters = false ;
1412
15- chrome . storage . local . get ( {
16- rest_port : default_rest_port ,
17- scrub_parameters : default_scrub_parameters
18- } , function ( items ) {
19- rest_port = items . rest_port
20- scrub_parameters = items . scrub_parameters
21- url_backend = "http://127.0.0.1:" + rest_port + "/add_record" // data will POST to here
22- } )
23-
24- function sendLog ( message , callback ) {
25- //console.log(message)
26- var xhr = new XMLHttpRequest ( )
27- xhr . open ( "POST" , url_backend , true )
28- xhr . send ( message + "\r\n" )
29- }
30-
31- // hashing function courtesy of bryc https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js
32- const cyrb53 = function ( str , seed = 42 ) {
33- let h1 = 0xdeadbeef ^ seed , h2 = 0x41c6ce57 ^ seed
13+ // hashing function courtesy of bryc https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js
14+ const cyrb53 = ( str , seed = 42 ) => {
15+ let h1 = 0xdeadbeef ^ seed , h2 = 0x41c6ce57 ^ seed ;
3416 for ( let i = 0 , ch ; i < str . length ; i ++ ) {
35- ch = str . charCodeAt ( i )
36- h1 = Math . imul ( h1 ^ ch , 2654435761 )
37- h2 = Math . imul ( h2 ^ ch , 1597334677 )
17+ ch = str . charCodeAt ( i ) ;
18+ h1 = Math . imul ( h1 ^ ch , 2654435761 ) ;
19+ h2 = Math . imul ( h2 ^ ch , 1597334677 ) ;
3820 }
39- h1 = Math . imul ( h1 ^ ( h1 >>> 16 ) , 2246822507 ) ^ Math . imul ( h2 ^ ( h2 >>> 13 ) , 3266489909 )
40- h2 = Math . imul ( h2 ^ ( h2 >>> 16 ) , 2246822507 ) ^ Math . imul ( h1 ^ ( h1 >>> 13 ) , 3266489909 )
41- return 4294967296 * ( 2097151 & h2 ) + ( h1 >>> 0 )
42- }
21+ h1 = Math . imul ( h1 ^ ( h1 >>> 16 ) , 2246822507 ) ^ Math . imul ( h2 ^ ( h2 >>> 13 ) , 3266489909 ) ;
22+ h2 = Math . imul ( h2 ^ ( h2 >>> 16 ) , 2246822507 ) ^ Math . imul ( h1 ^ ( h1 >>> 13 ) , 3266489909 ) ;
23+ return 4294967296 * ( 2097151 & h2 ) + ( h1 >>> 0 ) ;
24+ } ;
4325
4426function scrubber ( match , p1 , offset , string ) {
45- return "?SCRUBBED_hash=" + cyrb53 ( p1 )
27+ return "?SCRUBBED_hash=" + cyrb53 ( p1 ) ;
4628}
4729
48- function logResponse ( details ) {
49- if ( details . url == url_backend || details . tabId < 0 ) return // avoid feedback loop and other ugliness
50- //console.log(details)
51- var headers = details . responseHeaders
30+ async function logResponse ( details ) {
31+ // Get settings from storage every time, as the service worker can be terminated.
32+ const items = await chrome . storage . local . get ( {
33+ rest_port : default_rest_port ,
34+ scrub_parameters : default_scrub_parameters
35+ } ) ;
36+
37+ const url_backend = `http://127.0.0.1:${ items . rest_port } /add_record` ;
5238
53- var data = {
39+ // Avoid feedback loop and internal browser requests
40+ if ( details . url . startsWith ( url_backend ) || details . tabId < 0 ) return ;
41+
42+ const headers = details . responseHeaders ;
43+ let data = {
5444 url : details . url ,
5545 ts : details . timeStamp ,
5646 ip : details . ip ,
5747 method : details . method ,
5848 status : details . statusCode ,
5949 type : details . type
60- }
50+ } ;
6151
62- for ( var i = 0 , l = headers . length ; i < l ; ++ i ) {
63- header = headers [ i ] . name . toLowerCase ( )
64- if ( header == 'content-length' ) {
65- data . bytes = headers [ i ] . value
66- } else if ( header == 'content-type' ) {
67- data . content_type = headers [ i ] . value
52+ for ( const header of headers ) {
53+ const headerName = header . name . toLowerCase ( ) ;
54+ if ( headerName === 'content-length' ) {
55+ data . bytes = header . value ;
56+ } else if ( headerName === 'content-type' ) {
57+ data . content_type = header . value ;
6858 }
6959 }
7060
71- chrome . tabs . get ( details . tabId , function ( tab ) {
72- if ( ! chrome . runtime . lastError ) // sometimes the tab does not exist
73- data . referer = tab . url
61+ // Since chrome.tabs.get uses a callback, we wrap the final part of our logic in a new Promise
62+ // to keep the async/await flow clean.
63+ const finalizeAndSend = new Promise ( resolve => {
64+ chrome . tabs . get ( details . tabId , ( tab ) => {
65+ // Check lastError because the tab might have closed before this callback runs
66+ if ( ! chrome . runtime . lastError && tab ) {
67+ data . referer = tab . url ;
68+ }
7469
75- if ( scrub_parameters ) {
76- data . url = data . url . replace ( / \? .* / , scrubber )
77- if ( data . referer ) {
78- data . referer = data . referer . replace ( / \? .* / , scrubber )
79- }
80- }
70+ if ( items . scrub_parameters ) {
71+ data . url = data . url . replace ( / \? .* / , scrubber ) ;
72+ if ( data . referer ) {
73+ data . referer = data . referer . replace ( / \? .* / , scrubber ) ;
74+ }
75+ }
76+ resolve ( data ) ;
77+ } ) ;
78+ } ) ;
8179
82- sendLog ( JSON . stringify ( Object . assign ( data ) ) )
83- } )
80+ const finalData = await finalizeAndSend ;
81+
82+ try {
83+ await fetch ( url_backend , {
84+ method : 'POST' ,
85+ headers : {
86+ 'Content-Type' : 'application/json'
87+ } ,
88+ body : JSON . stringify ( finalData ) + "\r\n"
89+ } ) ;
90+ } catch ( error ) {
91+ console . error ( "HTTP Graph Error: Could not send data to backend." , error ) ;
92+ }
8493}
8594
8695chrome . webRequest . onCompleted . addListener (
8796 logResponse ,
8897 { urls : [ "http://*/*" , "https://*/*" ] } ,
8998 [ "responseHeaders" ]
90- )
99+ ) ;
91100
92- chrome . runtime . onInstalled . addListener ( function ( ) {
101+ chrome . runtime . onInstalled . addListener ( ( ) => {
93102 chrome . storage . local . set ( {
94103 rest_port : default_rest_port ,
95104 scrub_parameters : default_scrub_parameters
96- } )
97- } )
105+ } ) ;
106+ } ) ;
0 commit comments