2022-01-29 20:41:03 +01:00
import { EventEmitter } from 'events' ;
2022-01-29 20:50:44 +01:00
import React from 'react' ;
2022-08-13 22:42:50 +02:00
import { DataLocation , Sources } from './stolen-data-entry' ;
2021-10-03 09:03:56 +02:00
export type Unpromisify < T > = T extends Promise < infer R > ? R : T ;
export type Unarray < T > = T extends Array < infer R > ? R : T ;
export type Tab = Unarray < Unpromisify < ReturnType < typeof browser.tabs.query > >> ;
2021-11-09 17:47:42 +01:00
export type Request = {
2022-04-22 13:00:02 +02:00
cookieStoreId? : string ;
documentUrl? : string ; // RL of the document in which the resource will be loaded. For example, if the web page at "https://example.com" contains an image or an iframe, then the documentUrl for the image or iframe will be "https://example.com". For a top-level document, documentUrl is undefined.
frameId : number ;
incognito? : boolean ;
method : string ;
originUrl : string ;
parentFrameId : number ;
proxyInfo ? : {
host : string ;
port : number ;
type : string ;
username : string ;
proxyDNS : boolean ;
failoverTimeout : number ;
} ;
requestHeaders ? : { name : string ; value? : string ; binaryValue? : number [ ] } [ ] ;
requestId : string ;
tabId : number ;
thirdParty? : boolean ;
timeStamp : number ;
type : string ;
url : string ; // the target of the request;
urlClassification ? : { firstParty : string [ ] ; thirdParty : string [ ] } ;
2021-11-09 17:47:42 +01:00
} ;
2021-10-03 09:03:56 +02:00
export function getshorthost ( host : string ) {
2022-04-22 13:00:02 +02:00
const parts = host
. replace ( /^.*:\/\// , '' )
. replace ( /\/.*$/ , '' )
. split ( '.' ) ;
2022-07-09 15:28:37 +02:00
const second_last = parts . at ( - 2 ) ;
if ( ! second_last ) {
throw new Error ( 'url too short?' ) ;
}
let lookback = ! [ 'co' , 'com' ] . includes ( second_last ) ? - 2 : - 3 ;
2022-04-22 13:00:02 +02:00
if ( parts . at ( - 2 ) == 'doubleclick' || parts . at ( - 2 ) == 'google' ) {
lookback = - 4 ; // to distinguish between google ads and stats
} else if ( parts . at ( - 2 ) == 'google' ) {
lookback = - 3 ; // to distinguish various google services
}
return parts . slice ( lookback ) . join ( '.' ) ;
2021-10-03 09:03:56 +02:00
}
2021-11-07 09:17:19 +01:00
export function useEmitter (
2022-04-22 13:00:02 +02:00
e : EventEmitter
2022-02-07 15:28:01 +01:00
) : [
2022-04-22 13:00:02 +02:00
Record < string , number | undefined > ,
React . Dispatch < React.SetStateAction < Record < string , number | undefined > >>
2022-02-07 15:28:01 +01:00
] {
2022-04-22 13:00:02 +02:00
const [ eventCounts , setEventCounts ] = React . useState < Record < string , number | undefined > > ( {
'*' : 0 ,
} ) ;
React . useEffect ( ( ) = > {
const callback = ( eventSubtype : string ) = > {
setEventCounts ( ( eventCounts ) = > ( {
. . . eventCounts ,
. . . { [ eventSubtype ] : ( eventCounts [ eventSubtype ] || 0 ) + 1 } ,
. . . { '*' : ( eventCounts [ '*' ] === undefined ? 0 : eventCounts [ '*' ] ) + 1 } ,
} ) ) ;
} ;
e . on ( 'change' , callback ) ;
return ( ) = > {
e . removeListener ( 'change' , callback ) ;
} ;
} , [ ] ) ;
return [ eventCounts , setEventCounts ] ;
2021-10-03 09:03:56 +02:00
}
export function parseCookie ( cookie : string ) : Record < string , string > {
2022-04-22 13:00:02 +02:00
return cookie
. split ( ';' )
2022-08-31 09:50:37 +02:00
. map ( ( l ) = > [ l . slice ( 0 , l . indexOf ( '=' ) ) , l . slice ( l . indexOf ( '=' ) + 1 ) ] )
2022-04-22 13:00:02 +02:00
. reduce (
( acc , [ key , value ] ) = > ( {
. . . acc ,
[ key ] : value ,
} ) ,
{ }
) ;
2021-10-03 09:03:56 +02:00
}
2021-11-06 21:48:25 +01:00
export async function getTabByID ( id : number ) {
2022-04-22 13:00:02 +02:00
const tabs = await browser . tabs . query ( { currentWindow : true } ) ;
return tabs . find ( ( tab ) = > tab . id == id ) ;
2021-11-06 21:48:25 +01:00
}
2021-11-07 10:09:41 +01:00
2021-11-22 13:28:31 +01:00
export function parseToObject ( str : unknown ) : Record < string | symbol , unknown > {
2022-07-09 15:28:37 +02:00
let result : Record < string | symbol , unknown > = { } ;
2022-04-22 13:00:02 +02:00
let original_string : string ;
if ( typeof str === 'string' ) {
original_string = str ;
result = JSON . parse ( str ) ;
} else if ( typeof str == 'object' ) {
result = str as Record < string | symbol , unknown > ;
original_string = ( result [ Symbol . for ( 'originalString' ) ] as string ) || JSON . stringify ( str ) ;
2022-07-09 15:28:37 +02:00
} else {
2022-07-09 15:51:34 +02:00
return result ;
2022-04-22 13:00:02 +02:00
}
result [ Symbol . for ( 'originalString' ) ] = original_string ;
return result ;
2021-11-07 10:09:41 +01:00
}
2022-01-30 21:03:49 +01:00
export function isJSONObject ( str : unknown ) : str is Record < string , unknown > | string | number {
2022-04-22 13:00:02 +02:00
try {
const firstChar = JSON . stringify ( parseToObject ( str ) ) [ 0 ] ;
return [ '{' , '[' ] . includes ( firstChar ) ;
} catch ( e ) {
return false ;
}
2021-11-07 10:09:41 +01:00
}
export function isURL ( str : unknown ) : str is string {
2022-04-22 13:00:02 +02:00
try {
return ! ! ( typeof str === 'string' && new URL ( str ) ) ;
} catch ( e ) {
return false ;
}
2021-11-07 10:09:41 +01:00
}
export function hyphenate ( str : string ) : string {
2022-04-22 13:00:02 +02:00
return str . replace ( /[_\[A-Z]/g , ` ${ String . fromCharCode ( 173 ) } $ & ` ) ;
2021-11-07 10:09:41 +01:00
}
2021-11-07 13:57:24 +01:00
2021-11-09 17:47:42 +01:00
export function unique < T > ( array : T [ ] ) : Array < T > {
2022-04-22 13:00:02 +02:00
return Array . from ( new Set < T > ( array ) ) ;
2021-11-07 17:18:17 +01:00
}
export function allSubhosts ( host : string ) {
2022-04-22 13:00:02 +02:00
const parts = host . split ( '.' ) ;
const result = [ ] ;
for ( let i = 0 ; i < parts . length - 2 ; i ++ ) {
result . push ( parts . slice ( i ) . join ( '.' ) ) ;
}
return result ;
2021-11-07 17:18:17 +01:00
}
2021-11-07 17:44:22 +01:00
export function reduceConcat < T > ( a : T [ ] , b : T [ ] ) : T [ ] {
2022-04-22 13:00:02 +02:00
return a . concat ( b ) ;
2021-11-07 17:44:22 +01:00
}
2021-11-07 19:03:00 +01:00
export function getDate() {
2022-04-22 13:00:02 +02:00
const d = new Date ( ) ;
return ` ${ d . getFullYear ( ) } - ${ ( d . getMonth ( ) + 1 ) . toString ( ) . padStart ( 2 , '0' ) } - ${ d
. getDate ( )
. toString ( )
. padStart ( 2 , '0' ) } ` ;
2021-11-07 19:03:00 +01:00
}
2021-11-11 11:10:52 +01:00
export function toBase64 ( file : File ) : Promise < string > {
2022-07-09 15:28:37 +02:00
return new Promise ( ( resolve , reject ) = > {
2022-04-22 13:00:02 +02:00
const FR = new FileReader ( ) ;
FR . addEventListener ( 'load' , ( e ) = > {
2022-07-09 15:28:37 +02:00
const target = e . target ;
2022-07-09 15:51:34 +02:00
if ( ! target ) {
return reject ( 'File missing?' ) ;
}
resolve ( e . target . result as string ) ;
2022-04-22 13:00:02 +02:00
} ) ;
FR . readAsDataURL ( file ) ;
} ) ;
2021-11-11 11:10:52 +01:00
}
2021-11-21 18:19:58 +01:00
export function makeThrottle ( interval : number ) {
2022-04-22 13:00:02 +02:00
let last_emit = 0 ;
function emit ( callback : ( ) = > void ) {
if ( Date . now ( ) - last_emit > interval ) {
callback ( ) ;
last_emit = Date . now ( ) ;
return true ;
} else {
return false ;
}
}
return function ( callback : ( ) = > void ) {
if ( ! emit ( callback ) ) {
setTimeout ( ( ) = > emit ( callback ) , interval ) ;
}
} ;
2021-11-21 18:19:58 +01:00
}
2021-11-22 12:03:55 +01:00
export function isSameURL ( url1 : string , url2 : string ) : boolean {
2022-04-22 13:00:02 +02:00
if ( url1 === url2 ) {
return true ;
}
url1 = url1 . replace ( /^https?:\/\// , '' ) . replace ( /\/$/ , '' ) ;
url2 = url2 . replace ( /^https?:\/\// , '' ) . replace ( /\/$/ , '' ) ;
return url1 === url2 ;
2021-11-22 12:03:55 +01:00
}
2021-11-22 15:08:29 +01:00
export function isBase64 ( s : string ) : boolean {
2022-04-22 13:00:02 +02:00
try {
atob ( s ) ;
return true ;
} catch ( e ) { }
return false ;
2021-11-22 15:08:29 +01:00
}
export function isBase64JSON ( s : unknown ) : s is string {
2022-04-22 13:00:02 +02:00
return typeof s === 'string' && isBase64 ( s ) && isJSONObject ( atob ( s ) ) ;
2021-11-22 15:08:29 +01:00
}
2021-11-22 17:54:15 +01:00
export function flattenObject (
2022-04-22 13:00:02 +02:00
obj : unknown ,
2022-07-09 15:28:37 +02:00
parser : ( to_parse : { toString : ( ) = > string } ) = > string | Record < string , unknown > = ( id ) = >
id . toString ( ) ,
2022-04-22 13:00:02 +02:00
key = '' ,
ret = [ ] as [ string , string ] [ ] ,
parsed = false
2021-11-22 17:54:15 +01:00
) : [ string , string ] [ ] {
2022-04-22 13:00:02 +02:00
const prefix = key === '' ? '' : ` ${ key } . ` ;
if ( Array . isArray ( obj ) ) {
if ( obj . length == 1 ) {
flattenObject ( obj [ 0 ] , parser , key , ret ) ;
} else {
for ( let i in obj ) {
flattenObject ( obj [ i ] , parser , prefix + i , ret ) ;
}
}
} else if ( obj === null ) {
ret . push ( [ key , '' ] ) ;
} else if ( typeof obj === 'object' ) {
for ( const [ subkey , value ] of Object . entries ( obj ) ) {
flattenObject ( value , parser , prefix + subkey , ret ) ;
}
} else if ( ! parsed ) {
2022-07-09 15:51:34 +02:00
try {
flattenObject ( parser ( obj as { toString : ( ) = > string } ) , parser , key , ret , true ) ;
} catch ( e ) {
//emergency case, mostly for just type safety
ret . push ( [ key , JSON . stringify ( obj ) ] ) ;
}
2022-04-22 13:00:02 +02:00
} else if ( typeof obj === 'string' ) {
ret . push ( [ key , obj ] ) ;
} else {
throw new Error ( 'Something went wrong when parsing ' + obj ) ;
}
return ret ;
2021-11-22 17:54:15 +01:00
}
export function flattenObjectEntries (
2022-04-22 13:00:02 +02:00
entries : [ string , unknown ] [ ] ,
2022-07-09 15:28:37 +02:00
parser : ( to_parse : { toString : ( ) = > string } ) = > string | Record < string , unknown > = ( id ) = >
id . toString ( )
2021-11-22 17:54:15 +01:00
) : [ string , string ] [ ] {
2022-04-22 13:00:02 +02:00
return flattenObject ( Object . fromEntries ( entries ) , parser ) ;
2021-11-22 17:54:15 +01:00
}
2021-11-22 18:56:36 +01:00
export function maskString (
2022-04-22 13:00:02 +02:00
str : string ,
max_fraction_remaining : number ,
max_chars_total : number
2021-11-22 18:56:36 +01:00
) : string {
2022-04-22 13:00:02 +02:00
const amount_of_chars_to_cut =
str . length - Math . min ( str . length * max_fraction_remaining , max_chars_total ) ;
if ( amount_of_chars_to_cut == 0 ) {
return str ;
}
return (
str . slice ( 0 , str . length / 2 - amount_of_chars_to_cut / 2 ) +
'(...)' +
str . slice ( str . length / 2 + amount_of_chars_to_cut / 2 )
) ;
2021-11-22 18:56:36 +01:00
}
2021-11-26 20:58:31 +01:00
export function safeDecodeURIComponent ( s : string ) {
2022-04-22 13:00:02 +02:00
try {
return decodeURIComponent ( s ) ;
} catch ( e ) {
return s ;
}
2021-11-26 20:58:31 +01:00
}
2022-01-30 21:03:49 +01:00
export function normalizeForClassname ( string : string ) {
2022-04-22 13:00:02 +02:00
return string . replace ( /[^a-z0-9]/gi , '-' ) ;
2022-02-10 19:54:51 +01:00
}
export function wordlist ( words : string [ ] ) {
2022-08-13 22:42:50 +02:00
return Array . from ( new Set ( words ) ) . reduce (
( acc , word , i ) = >
` ${ acc } ${
i > 0 ? ( i < words . length - 1 ? ', ' : Math . random ( ) > 0.5 ? ' i ' : ' oraz ' ) : ''
} $ { word } ` ,
2022-04-22 13:00:02 +02:00
''
) ;
2022-01-30 21:03:49 +01:00
}
2022-08-13 22:42:50 +02:00
const source_to_word : Record < Sources , string > = {
cookie : 'plik cookie o nazwie' ,
pathname : 'fragment ścieżki w URL' ,
queryparams : 'query params w URL o nazwie' ,
header : 'nagłówek HTTP' ,
request_body : 'body zapytania HTTP, pod kluczem' ,
} ;
export function dataLocationToText ( l : DataLocation ) {
return ` ${ source_to_word [ l . source ] } ${ l . key } ` ;
}
export function downloadText ( filename : string , text : string ) {
// https://stackoverflow.com/questions/45831191/generate-and-download-file-from-js
var element = document . createElement ( 'a' ) ;
element . setAttribute ( 'href' , 'data:text/plain;charset=utf-8,' + encodeURIComponent ( text ) ) ;
element . setAttribute ( 'download' , filename ) ;
element . style . display = 'none' ;
document . body . appendChild ( element ) ;
element . click ( ) ;
document . body . removeChild ( element ) ;
}