Cleanup, add second button
This commit is contained in:
		
							parent
							
								
									55b3bb0941
								
							
						
					
					
						commit
						734f418667
					
				
							
								
								
									
										3
									
								
								assets/icons/data.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/icons/data.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||
| <path d="M12 22C7.664 22 4 19.965 4 17.556V6.444C4 4.035 7.664 2 12 2C16.336 2 20 4.035 20 6.444V17.556C20 19.965 16.337 22 12 22ZM6 14.9V17.559C6.07 18.112 8.309 19.781 12 19.781C15.691 19.781 17.931 18.107 18 17.553V14.9C16.1794 15.9554 14.1039 16.4905 12 16.447C9.89606 16.4906 7.82058 15.9554 6 14.9ZM6 9.341V12C6.07 12.553 8.309 14.222 12 14.222C15.691 14.222 17.931 12.548 18 11.994V9.341C16.1795 10.3968 14.104 10.9323 12 10.889C9.89596 10.9323 7.82046 10.3968 6 9.341ZM12 4.222C8.308 4.222 6.069 5.896 6 6.451C6.07 7 8.311 8.666 12 8.666C15.689 8.666 17.931 6.992 18 6.438C17.93 5.887 15.689 4.222 12 4.222Z" fill="#2E3A59"/> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 736 B | 
| @ -25,5 +25,3 @@ esbuild | ||||
|         }, | ||||
|     }) | ||||
|     .catch(() => process.exit(1)); | ||||
| 
 | ||||
| //   npx esbuild sidebar/sidebar.tsx test.ts --bundle report-window/report-window.tsx --bundle background.ts --bundle --outdir=./lib
 | ||||
|  | ||||
							
								
								
									
										170
									
								
								memory.ts
									
									
									
									
									
								
							
							
						
						
									
										170
									
								
								memory.ts
									
									
									
									
									
								
							| @ -1,101 +1,107 @@ | ||||
| import ExtendedRequest from "./extended-request"; | ||||
| import { getshorthost, makeThrottle } from "./util"; | ||||
| import { EventEmitter } from "events"; | ||||
| import { RequestCluster } from "./request-cluster"; | ||||
| import ExtendedRequest from './extended-request'; | ||||
| import { getshorthost, makeThrottle } from './util'; | ||||
| import { EventEmitter } from 'events'; | ||||
| import { RequestCluster } from './request-cluster'; | ||||
| 
 | ||||
| export default class Memory extends EventEmitter { | ||||
|   origin_to_history = {} as Record<string, Record<string, RequestCluster>>; | ||||
|   private throttle = makeThrottle(200); | ||||
|   async register(request: ExtendedRequest) { | ||||
|     await request.init(); | ||||
|     // console.log("registering request for", request.origin);
 | ||||
|     if (!request.isThirdParty()) { | ||||
|       return; | ||||
|     origin_to_history = {} as Record<string, Record<string, RequestCluster>>; | ||||
|     private throttle = makeThrottle(200); | ||||
|     async register(request: ExtendedRequest) { | ||||
|         await request.init(); | ||||
|         if (!request.isThirdParty()) { | ||||
|             return; | ||||
|         } | ||||
|         if (!this.origin_to_history[request.origin]) { | ||||
|             this.origin_to_history[request.origin] = {}; | ||||
|         } | ||||
|         const shorthost = getshorthost(new URL(request.url).host); | ||||
|         if (!this.origin_to_history[request.origin][shorthost]) { | ||||
|             const cluster = new RequestCluster(shorthost); | ||||
|             this.origin_to_history[request.origin][shorthost] = cluster; | ||||
|         } | ||||
|         this.origin_to_history[request.origin][shorthost].add(request); | ||||
|         this.emit('change'); | ||||
|     } | ||||
|     if (!this.origin_to_history[request.origin]) { | ||||
|       this.origin_to_history[request.origin] = {}; | ||||
| 
 | ||||
|     constructor() { | ||||
|         super(); | ||||
| 
 | ||||
|         browser.webRequest.onBeforeRequest.addListener( | ||||
|             async (request) => { | ||||
|                 new ExtendedRequest(request); | ||||
|             }, | ||||
|             { urls: ['<all_urls>'] }, | ||||
|             ['requestBody'] | ||||
|         ); | ||||
|         browser.webRequest.onBeforeSendHeaders.addListener( | ||||
|             async (request) => { | ||||
|                 const extendedRequest = ExtendedRequest.by_id[ | ||||
|                     request.requestId | ||||
|                 ].addHeaders(request.requestHeaders || []); | ||||
|                 this.register(extendedRequest); | ||||
|             }, | ||||
|             { urls: ['<all_urls>'] }, | ||||
|             ['requestHeaders'] | ||||
|         ); | ||||
|     } | ||||
|     const shorthost = getshorthost(new URL(request.url).host); | ||||
|     if (!this.origin_to_history[request.origin][shorthost]) { | ||||
|       const cluster = new RequestCluster(shorthost); | ||||
|       this.origin_to_history[request.origin][shorthost] = cluster; | ||||
| 
 | ||||
|     emit(eventName: string, immediate = false) { | ||||
|         try { | ||||
|             if (immediate) { | ||||
|                 super.emit(eventName); | ||||
|                 return; | ||||
|             } else { | ||||
|                 this.throttle(() => super.emit(eventName)); | ||||
|             } | ||||
|             return true; | ||||
|         } catch (e) { | ||||
|             //   debugger;
 | ||||
|             console.error(e); | ||||
|         } | ||||
|     } | ||||
|     this.origin_to_history[request.origin][shorthost].add(request); | ||||
|     this.emit("change"); | ||||
|   } | ||||
| 
 | ||||
|   constructor() { | ||||
|     super(); | ||||
| 
 | ||||
|     browser.webRequest.onBeforeRequest.addListener( | ||||
|       async (request) => { | ||||
|         new ExtendedRequest(request); | ||||
|       }, | ||||
|       { urls: ["<all_urls>"] }, | ||||
|       ["requestBody"] | ||||
|     ); | ||||
|     browser.webRequest.onBeforeSendHeaders.addListener( | ||||
|       async (request) => { | ||||
|         const extendedRequest = ExtendedRequest.by_id[ | ||||
|           request.requestId | ||||
|         ].addHeaders(request.requestHeaders || []); | ||||
|         this.register(extendedRequest); | ||||
|       }, | ||||
|       { urls: ["<all_urls>"] }, | ||||
|       ["requestHeaders"] | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   emit(eventName: string, immediate = false) { | ||||
|     try { | ||||
|       if (immediate) { | ||||
|         super.emit(eventName); | ||||
|         return; | ||||
|       } else { | ||||
|         this.throttle(() => super.emit(eventName)); | ||||
|       } | ||||
|       return true; | ||||
|     } catch (e) { | ||||
|     //   debugger;
 | ||||
|     getClustersForOrigin(origin: string): Record<string, RequestCluster> { | ||||
|         return this.origin_to_history[origin] || {}; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   getClustersForOrigin(origin: string): Record<string, RequestCluster> { | ||||
|     return this.origin_to_history[origin] || {}; | ||||
|   } | ||||
|     async removeCookiesFor(origin: string, shorthost?: string): Promise<void> { | ||||
|         if (shorthost) { | ||||
|             const cookies = await browser.cookies.getAll({ domain: shorthost }); | ||||
|             for (const cookie of cookies) { | ||||
|                 console.log( | ||||
|                     'removing cookie', | ||||
|                     cookie.name, | ||||
|                     'from', | ||||
|                     cookie.domain | ||||
|                 ); | ||||
|                 await browser.cookies.remove({ | ||||
|                     name: cookie.name, | ||||
|                     url: `https://${cookie.domain}`, | ||||
|                 }); | ||||
|             } | ||||
|         } else { | ||||
|             const clusters = this.getClustersForOrigin(origin); | ||||
| 
 | ||||
|   async removeCookiesFor(origin: string, shorthost?: string): Promise<void> { | ||||
|     if (shorthost) { | ||||
|       const cookies = await browser.cookies.getAll({ domain: shorthost }); | ||||
|       for (const cookie of cookies) { | ||||
|         console.log("removing cookie", cookie.name, "from", cookie.domain); | ||||
|         await browser.cookies.remove({ | ||||
|           name: cookie.name, | ||||
|           url: `https://${cookie.domain}`, | ||||
|         }); | ||||
|       } | ||||
|     } else { | ||||
|       const clusters = this.getClustersForOrigin(origin); | ||||
| 
 | ||||
|       await Promise.all( | ||||
|         Object.values(clusters) | ||||
|           .filter((cluster) => !shorthost || cluster.id === shorthost) | ||||
|           .map((cluster) => this.removeCookiesFor(origin, cluster.id)) | ||||
|       ); | ||||
|             await Promise.all( | ||||
|                 Object.values(clusters) | ||||
|                     .filter((cluster) => !shorthost || cluster.id === shorthost) | ||||
|                     .map((cluster) => this.removeCookiesFor(origin, cluster.id)) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   async removeRequestsFor(origin: string) { | ||||
|     this.origin_to_history[origin] = {}; | ||||
|   } | ||||
|     async removeRequestsFor(origin: string) { | ||||
|         this.origin_to_history[origin] = {}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export function init() { | ||||
|   const memory = new Memory(); | ||||
|     const memory = new Memory(); | ||||
| 
 | ||||
|   (window as any).memory = memory; | ||||
|     (window as any).memory = memory; | ||||
| } | ||||
| 
 | ||||
| export function getMemory(): Memory { | ||||
|   return (browser.extension.getBackgroundPage().window as any).memory as Memory; | ||||
|     return (browser.extension.getBackgroundPage().window as any) | ||||
|         .memory as Memory; | ||||
| } | ||||
|  | ||||
| @ -114,7 +114,6 @@ function Report() { | ||||
|     console.time('rendering template'); | ||||
|     const result = ( | ||||
|         <div {...{ 'data-version': counter }}> | ||||
|             {/*<DataPreview {...{entries, refresh}} */} | ||||
|             <h1>Generuj treść maila dla {origin}</h1> | ||||
|             <EmailTemplate {...{ entries, clusters, version: counter }} /> | ||||
|             <HARConverter {...{ entries }} /> | ||||
|  | ||||
| @ -10,6 +10,8 @@ import TrashIcon from '../assets/icons/trash_full.svg'; | ||||
| import MailIcon from '../assets/icons/mail.svg'; | ||||
| import ShortLeftIcon from '../assets/icons/short_left.svg'; | ||||
| import CloseBigIcon from '../assets/icons/close_big.svg'; | ||||
| import CookiesIcon from '../assets/icons/cookie.svg'; | ||||
| import DataIcon from '../assets/icons/data.svg'; | ||||
| 
 | ||||
| async function getCurrentTab() { | ||||
|     const [tab] = await browser.tabs.query({ | ||||
| @ -75,15 +77,6 @@ const Sidebar = () => { | ||||
| 
 | ||||
|     return ( | ||||
|         <div className="sidebar"> | ||||
|             {/* <div id="selector"> | ||||
|         <TabDropdown setPickedTab={setPickedTab} pickedTab={pickedTab} /> | ||||
|         <button | ||||
|           id="get_current_tab_button" | ||||
|           onClick={async () => setPickedTab(await getCurrentTab())} | ||||
|         > | ||||
|           Wybierz aktywną kartę{" "} | ||||
|         </button> | ||||
|       </div> */} | ||||
|             <header | ||||
|                 className={ | ||||
|                     logoVisibility ? 'header' : 'header header--without-logo' | ||||
| @ -126,23 +119,9 @@ const Sidebar = () => { | ||||
|             {stolenDataView ? ( | ||||
|                 <nav> | ||||
|                     <button onClick={() => setStolenDataView(!stolenDataView)}> | ||||
|                         {/* {stolenDataView ? 'Options' : 'Data'} | ||||
|                          */} | ||||
|                         <SettingsIcon width={20} height={20} /> | ||||
|                         <span>Ustawienia wtyczki</span> | ||||
|                         <span>Ustawienia</span> | ||||
|                     </button> | ||||
|                     {/* <button | ||||
|                     onClick={() => { | ||||
|                         getMemory().removeCookiesFor( | ||||
|                             origin, | ||||
|                             getshorthost(new URL(origin).host) | ||||
|                         ); | ||||
|                         setMarksOccurrence(false); | ||||
|                     }} | ||||
|                 > | ||||
|                     <TrashIcon /> | ||||
|                     <span>Wyczyść ciasteczka first-party</span> | ||||
|                 </button> */} | ||||
|                     <button | ||||
|                         onClick={() => { | ||||
|                             getMemory().removeRequestsFor(origin); | ||||
| @ -150,9 +129,21 @@ const Sidebar = () => { | ||||
|                             setMarksOccurrence(false); | ||||
|                         }} | ||||
|                     > | ||||
|                         {/* {stolenDataView ? 'Options' : 'Data'} | ||||
|                          */} | ||||
|                         <TrashIcon width={20} height={20} /> | ||||
|                         <span>Wyczyść historię wtyczki</span> | ||||
|                     </button> | ||||
|                     <button | ||||
|                         onClick={() => { | ||||
|                             getMemory().removeCookiesFor(origin); | ||||
|                             // getMemory().removeCookiesFor(
 | ||||
|                             //     origin,
 | ||||
|                             //     getshorthost(new URL(origin).host)
 | ||||
|                             // );
 | ||||
|                             setCounter((c) => c + 1); | ||||
|                             setMarksOccurrence(false); | ||||
|                         }} | ||||
|                     > | ||||
|                         <CookiesIcon width={20} height={20} /> | ||||
|                         <span>Wyczyść ciasteczka</span> | ||||
|                     </button> | ||||
|                     <button | ||||
| @ -177,7 +168,7 @@ const Sidebar = () => { | ||||
|                     > | ||||
|                         <MailIcon width={20} height={20} /> | ||||
|                         <span> | ||||
|                             Utwórz wiadomość dla administratora tej witryny | ||||
|                             Utwórz wiadomość dla administratora witryny | ||||
|                         </span> | ||||
|                     </button> | ||||
|                 </nav> | ||||
| @ -232,8 +223,6 @@ const Sidebar = () => { | ||||
|                     /> | ||||
|                 )} | ||||
|             </section> | ||||
| 
 | ||||
|             {/* <footer>Footer marks → {JSON.stringify(marksOccurrence)}</footer> */} | ||||
|         </div> | ||||
|     ); | ||||
| }; | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import React, { Fragment } from 'react'; | ||||
| import React from 'react'; | ||||
| import { getMemory } from '../memory'; | ||||
| import { StolenDataEntry } from '../stolen-data-entry'; | ||||
| 
 | ||||
| @ -38,7 +38,6 @@ function StolenDataValue({ | ||||
|                 e.stopPropagation(); | ||||
|             }} | ||||
|             title={maskString(entry.value, 1, MAX_STRING_VALUE_LENGTH)} | ||||
|             // style={{ color: entry.isMarked ? 'black' : 'gray' }}
 | ||||
|         > | ||||
|             {body} | ||||
|         </td> | ||||
| @ -120,8 +119,6 @@ function StolenDataRow({ | ||||
|                     </span> | ||||
|                 ) : null} | ||||
|             </td> | ||||
|             {/* <td style={{ wordWrap: 'anywhere' as any }}> */} | ||||
| 
 | ||||
|             <StolenDataValue refresh={refresh} entry={entry} /> | ||||
|         </tr> | ||||
|     ); | ||||
| @ -194,56 +191,5 @@ export default function StolenDataCluster({ | ||||
|                 </table> | ||||
|             </section> | ||||
|         </div> | ||||
| 
 | ||||
|         // <div>
 | ||||
|         //     <h2>
 | ||||
|         //         <a href={'https://' + cluster.id}>{cluster.id}</a>{' '}
 | ||||
|         //         {cluster.hasCookies() ? '🍪' : ''} x{cluster.requests.length}{' '}
 | ||||
|         //         {/* <a
 | ||||
|         //          *   href="#"
 | ||||
|         //          *   style={{ fontSize: "10px" }}
 | ||||
|         //          *   onClick={() => getMemory().removeCookiesFor(origin, shorthost)}
 | ||||
|         //          * >
 | ||||
|         //          *   Wyczyść cookiesy
 | ||||
|         //          * </a> */}
 | ||||
|         //         <a
 | ||||
|         //             href="#"
 | ||||
|         //             style={{ fontSize: '10px' }}
 | ||||
|         //             onClick={(e) => {
 | ||||
|         //                 cluster.autoMark();
 | ||||
|         //                 refresh();
 | ||||
|         //                 e.preventDefault();
 | ||||
|         //             }}
 | ||||
|         //         >
 | ||||
|         //             Zaznacz auto
 | ||||
|         //         </a>
 | ||||
|         //     </h2>
 | ||||
|         //     <div>
 | ||||
|         //         {cluster.getFullHosts().map((host) => (
 | ||||
|         //             <a key={host} href={`https://${host}`}>
 | ||||
|         //                 {host},{' '}
 | ||||
|         //             </a>
 | ||||
|         //         ))}
 | ||||
|         //     </div>
 | ||||
|         //     <table>
 | ||||
|         //         <tbody>
 | ||||
|         //             {cluster
 | ||||
|         //                 .calculateRepresentativeStolenData({
 | ||||
|         //                     minValueLength,
 | ||||
|         //                     cookiesOnly,
 | ||||
|         //                     cookiesOrOriginOnly,
 | ||||
|         //                 })
 | ||||
|         //                 .map((entry) => (
 | ||||
|         //                     <StolenDataRow
 | ||||
|         //                         refresh={refresh}
 | ||||
|         //                         {...{
 | ||||
|         //                             entry,
 | ||||
|         //                             key: entry.id,
 | ||||
|         //                         }}
 | ||||
|         //                     />
 | ||||
|         //                 ))}
 | ||||
|         //         </tbody>
 | ||||
|         //     </table>
 | ||||
|         // </div>
 | ||||
|     ); | ||||
| } | ||||
|  | ||||
| @ -40,49 +40,6 @@ export function StolenData({ | ||||
|         ); | ||||
|     return ( | ||||
|         <div className="stolen-data-container"> | ||||
|             {/* <button | ||||
|                     style={{ marginLeft: '1rem' }} | ||||
|                     onClick={() => | ||||
|                         getMemory().removeCookiesFor( | ||||
|                             origin, | ||||
|                             getshorthost(new URL(origin).host) | ||||
|                         ) | ||||
|                     } | ||||
|                 > | ||||
|                     Wyczyść cookiesy 1st party | ||||
|                 </button> | ||||
|                 <button | ||||
|                     style={{ marginLeft: '1rem' }} | ||||
|                     onClick={() => { | ||||
|                         getMemory().removeRequestsFor(origin); | ||||
|                         refresh(); | ||||
|                     }} | ||||
|                 > | ||||
|                     Wyczyść pamięć | ||||
|                 </button> */} | ||||
| 
 | ||||
|             {/* <button | ||||
|                     style={{ marginLeft: '1rem' }} | ||||
|                     onClick={() => | ||||
|                         window.open( | ||||
|                             `/report-window/report-window.html?origin=${origin}`, | ||||
|                             'new_window', | ||||
|                             'width=800,height=600' | ||||
|                         ) | ||||
|                     } | ||||
|                 > | ||||
|                     Generuj maila | ||||
|                 </button> */} | ||||
| 
 | ||||
|             {/* <button | ||||
|                     onClick={() => { | ||||
|                         clusters.forEach((cluster) => cluster.autoMark()); | ||||
|                         refresh(); | ||||
|                     }} | ||||
|                 > | ||||
|                     Zaznacz automatycznie | ||||
|                 </button> */} | ||||
| 
 | ||||
|             <span>Domeny oraz przesłane informacje</span> | ||||
| 
 | ||||
|             {clusters.map((cluster) => { | ||||
|  | ||||
| @ -1,265 +1,269 @@ | ||||
| // import { TCModel } from "@iabtcf/core";
 | ||||
| import { EventEmitter } from "events"; | ||||
| import ExtendedRequest, { HAREntry } from "./extended-request"; | ||||
| import { EventEmitter } from 'events'; | ||||
| import ExtendedRequest, { HAREntry } from './extended-request'; | ||||
| 
 | ||||
| import { | ||||
|   getshorthost, | ||||
|   isBase64, | ||||
|   isBase64JSON, | ||||
|   isJSONObject, | ||||
|   isURL, | ||||
|   maskString, | ||||
|   parseToObject, | ||||
|   safeDecodeURIComponent, | ||||
| } from "./util"; | ||||
|     getshorthost, | ||||
|     isBase64, | ||||
|     isBase64JSON, | ||||
|     isJSONObject, | ||||
|     isURL, | ||||
|     maskString, | ||||
|     parseToObject, | ||||
|     safeDecodeURIComponent, | ||||
| } from './util'; | ||||
| 
 | ||||
| export type Sources = | ||||
|   | "cookie" | ||||
|   | "pathname" | ||||
|   | "queryparams" | ||||
|   | "header" | ||||
|   | "request_body"; | ||||
|     | 'cookie' | ||||
|     | 'pathname' | ||||
|     | 'queryparams' | ||||
|     | 'header' | ||||
|     | 'request_body'; | ||||
| 
 | ||||
| export const Classifications = <const>{ | ||||
|   id: "Identyfikator internetowy", | ||||
|   history: "Część historii przeglądania", | ||||
|   location: "Informacje na temat mojego położenia", | ||||
|     id: 'Identyfikator internetowy', | ||||
|     history: 'Część historii przeglądania', | ||||
|     location: 'Informacje na temat mojego położenia', | ||||
| }; | ||||
| 
 | ||||
| const ID_PREVIEW_MAX_LENGTH = 20; | ||||
| const MIN_COOKIE_LENGTH_FOR_AUTO_MARK = 15; | ||||
| 
 | ||||
| const id = (function* id() { | ||||
|   let i = 0; | ||||
|   while (true) { | ||||
|     i++; | ||||
|     yield i; | ||||
|   } | ||||
|     let i = 0; | ||||
|     while (true) { | ||||
|         i++; | ||||
|         yield i; | ||||
|     } | ||||
| })(); | ||||
| 
 | ||||
| export type DecodingSchema = "base64" | "raw"; | ||||
| export type DecodingSchema = 'base64' | 'raw'; | ||||
| 
 | ||||
| export class StolenDataEntry extends EventEmitter { | ||||
|   public isIAB = false; | ||||
|   // public iab: TCModel | null = null;
 | ||||
|   public id: number; | ||||
|   private marked = false; | ||||
|   public classification: keyof typeof Classifications; | ||||
|   public decoding_applied: DecodingSchema = "raw"; | ||||
|   public decodings_available: DecodingSchema[] = ["raw"]; | ||||
|     public isIAB = false; | ||||
|     public id: number; | ||||
|     private marked = false; | ||||
|     public classification: keyof typeof Classifications; | ||||
|     public decoding_applied: DecodingSchema = 'raw'; | ||||
|     public decodings_available: DecodingSchema[] = ['raw']; | ||||
| 
 | ||||
|   constructor( | ||||
|     public request: ExtendedRequest, | ||||
|     public source: Sources, | ||||
|     public name: string, | ||||
|     public value: string | ||||
|   ) { | ||||
|     // try {
 | ||||
|     //   this.iab = TCString.decode(value);
 | ||||
|     //   // console.log(this.iab);
 | ||||
|     //   this.isIAB = true;
 | ||||
|     // } catch (e) {}
 | ||||
|     super(); | ||||
|     this.id = id.next().value as number; | ||||
|     this.classification = this.classify(); | ||||
|     if (isBase64(value)) { | ||||
|       this.decodings_available.push("base64"); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   getPriority() { | ||||
|     let priority = 0; | ||||
|     priority += Math.min(this.value.length, 50); | ||||
|     const url = new URL(this.request.originalURL); | ||||
|     if (this.value.includes(url.host)) { | ||||
|       priority += 100; | ||||
|     } | ||||
|     if (this.value.includes(url.pathname)) { | ||||
|       priority += 100; | ||||
|     } | ||||
|     if (this.source === "cookie") { | ||||
|       priority += 200; | ||||
|     } | ||||
|     return priority; | ||||
|   } | ||||
| 
 | ||||
|   get isMarked() { | ||||
|     return this.marked; | ||||
|   } | ||||
| 
 | ||||
|   hasValue(value: string) { | ||||
|     return this.value === value; | ||||
|   } | ||||
| 
 | ||||
|   static parseValue(value: unknown): string | Record<string, unknown> { | ||||
|     if (isBase64JSON(value)) { | ||||
|       return StolenDataEntry.parseValue({ base64: JSON.parse(atob(value)) }); | ||||
|     } | ||||
|     if (value === undefined) { | ||||
|       return ""; | ||||
|     } | ||||
|     if (isJSONObject(value)) { | ||||
|       const object = parseToObject(value); | ||||
|       return object; | ||||
|     } else if (isURL(value)) { | ||||
|       const url = new URL(value); | ||||
|       let hash = url.hash; | ||||
|       if (hash.includes("=")) { | ||||
|         //facebook sometimes includes querystring-encoded data into the hash... attempt to parse it
 | ||||
|         try { | ||||
|           hash = Object.fromEntries( | ||||
|             hash | ||||
|               .slice(1) | ||||
|               .split("&") | ||||
|               .map((kv) => kv.split("=")) | ||||
|           ); | ||||
|         } catch (e) { | ||||
|           // failed to parse as query string
 | ||||
|           console.log( | ||||
|             "Failed attempt to parse hash location as query string, probably safe to ignore:", | ||||
|             e | ||||
|           ); | ||||
|     constructor( | ||||
|         public request: ExtendedRequest, | ||||
|         public source: Sources, | ||||
|         public name: string, | ||||
|         public value: string | ||||
|     ) { | ||||
|         super(); | ||||
|         this.id = id.next().value as number; | ||||
|         this.classification = this.classify(); | ||||
|         if (isBase64(value)) { | ||||
|             this.decodings_available.push('base64'); | ||||
|         } | ||||
|       } | ||||
|       const searchParams = Object.fromEntries( | ||||
|         ( | ||||
|           url.searchParams as unknown as { | ||||
|             entries: () => Iterable<[string, string]>; | ||||
|           } | ||||
|         ).entries() | ||||
|       ); | ||||
|       if (typeof hash !== "object" && Object.keys(searchParams).length === 0) { | ||||
|         return value; // just a string;
 | ||||
|       } | ||||
|       const object = { | ||||
|         [Symbol.for("originalString")]: value, // so it doesn't appear raw in the table but can be easily retrieved later
 | ||||
|         host: url.host, | ||||
|         path: url.pathname, | ||||
|         searchParams, | ||||
|         ...(hash === "" ? {} : typeof hash === "string" ? { hash } : hash), | ||||
|       }; | ||||
|       return object; | ||||
|     } else if (value === null) { | ||||
|       return "null"; | ||||
|     } else { | ||||
|       return value.toString(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   getParsedValue(key_path: string): string | Record<string | symbol, unknown> { | ||||
|     let object = StolenDataEntry.parseValue(this.value); | ||||
|     for (const key of key_path.split(".")) { | ||||
|       if (key === "") continue; | ||||
|       object = StolenDataEntry.parseValue(object[key]); | ||||
|     getPriority() { | ||||
|         let priority = 0; | ||||
|         priority += Math.min(this.value.length, 50); | ||||
|         const url = new URL(this.request.originalURL); | ||||
|         if (this.value.includes(url.host)) { | ||||
|             priority += 100; | ||||
|         } | ||||
|         if (this.value.includes(url.pathname)) { | ||||
|             priority += 100; | ||||
|         } | ||||
|         if (this.source === 'cookie') { | ||||
|             priority += 200; | ||||
|         } | ||||
|         return priority; | ||||
|     } | ||||
|     return object; | ||||
|   } | ||||
| 
 | ||||
|   mark() { | ||||
|     const had_been_marked_before = this.marked; | ||||
|     this.marked = true; | ||||
|     if (!had_been_marked_before) { | ||||
|       this.emit("change"); | ||||
|     get isMarked() { | ||||
|         return this.marked; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   unmark() { | ||||
|     const had_been_marked_before = this.marked; | ||||
|     this.marked = false; | ||||
|     if (had_been_marked_before) { | ||||
|       this.emit("change"); | ||||
|     hasValue(value: string) { | ||||
|         return this.value === value; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   toggleMark() { | ||||
|     if (this.marked) { | ||||
|       this.unmark(); | ||||
|     } else { | ||||
|       this.mark(); | ||||
|     static parseValue(value: unknown): string | Record<string, unknown> { | ||||
|         if (isBase64JSON(value)) { | ||||
|             return StolenDataEntry.parseValue({ | ||||
|                 base64: JSON.parse(atob(value)), | ||||
|             }); | ||||
|         } | ||||
|         if (value === undefined) { | ||||
|             return ''; | ||||
|         } | ||||
|         if (isJSONObject(value)) { | ||||
|             const object = parseToObject(value); | ||||
|             return object; | ||||
|         } else if (isURL(value)) { | ||||
|             const url = new URL(value); | ||||
|             let hash = url.hash; | ||||
|             if (hash.includes('=')) { | ||||
|                 //facebook sometimes includes querystring-encoded data into the hash... attempt to parse it
 | ||||
|                 try { | ||||
|                     hash = Object.fromEntries( | ||||
|                         hash | ||||
|                             .slice(1) | ||||
|                             .split('&') | ||||
|                             .map((kv) => kv.split('=')) | ||||
|                     ); | ||||
|                 } catch (e) { | ||||
|                     // failed to parse as query string
 | ||||
|                     console.log( | ||||
|                         'Failed attempt to parse hash location as query string, probably safe to ignore:', | ||||
|                         e | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|             const searchParams = Object.fromEntries( | ||||
|                 ( | ||||
|                     url.searchParams as unknown as { | ||||
|                         entries: () => Iterable<[string, string]>; | ||||
|                     } | ||||
|                 ).entries() | ||||
|             ); | ||||
|             if ( | ||||
|                 typeof hash !== 'object' && | ||||
|                 Object.keys(searchParams).length === 0 | ||||
|             ) { | ||||
|                 return value; // just a string;
 | ||||
|             } | ||||
|             const object = { | ||||
|                 [Symbol.for('originalString')]: value, // so it doesn't appear raw in the table but can be easily retrieved later
 | ||||
|                 host: url.host, | ||||
|                 path: url.pathname, | ||||
|                 searchParams, | ||||
|                 ...(hash === '' | ||||
|                     ? {} | ||||
|                     : typeof hash === 'string' | ||||
|                     ? { hash } | ||||
|                     : hash), | ||||
|             }; | ||||
|             return object; | ||||
|         } else if (value === null) { | ||||
|             return 'null'; | ||||
|         } else { | ||||
|             return value.toString(); | ||||
|         } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private classify(): keyof typeof Classifications { | ||||
|     let result: keyof typeof Classifications; | ||||
|     if (this.exposesOrigin()) { | ||||
|       result = "history"; | ||||
|     } else { | ||||
|       result = "id"; | ||||
|     getParsedValue( | ||||
|         key_path: string | ||||
|     ): string | Record<string | symbol, unknown> { | ||||
|         let object = StolenDataEntry.parseValue(this.value); | ||||
|         for (const key of key_path.split('.')) { | ||||
|             if (key === '') continue; | ||||
|             object = StolenDataEntry.parseValue(object[key]); | ||||
|         } | ||||
|         return object; | ||||
|     } | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   isRelatedToID() { | ||||
|     return this.request.stolenData.some( | ||||
|       (entry) => entry.classification == "id" | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   matchesHAREntry(har: HAREntry): boolean { | ||||
|     return this.request.matchesHAREntry(har); | ||||
|   } | ||||
| 
 | ||||
|   getValuePreview(key = ""): string { | ||||
|     const value = this.getParsedValue(key); | ||||
|     const str = | ||||
|       typeof value === "object" && value[Symbol.for("originalString")] | ||||
|         ? (value[Symbol.for("originalString")] as string) | ||||
|         : value.toString(); | ||||
|     if (typeof value !== "object" && this.classification == "id") { | ||||
|       return maskString(value, 1 / 3, ID_PREVIEW_MAX_LENGTH); | ||||
|     } else if ( | ||||
|       typeof value === "object" && | ||||
|       value[Symbol.for("originalString")] | ||||
|     ) { | ||||
|       return value[Symbol.for("originalString")] as string; | ||||
|     } else { | ||||
|       return str; | ||||
|     mark() { | ||||
|         const had_been_marked_before = this.marked; | ||||
|         this.marked = true; | ||||
|         if (!had_been_marked_before) { | ||||
|             this.emit('change'); | ||||
|         } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   getUniqueKey() { | ||||
|     return this.request.shorthost + ";" + this.name + ";" + this.value; | ||||
|   } | ||||
| 
 | ||||
|   exposesOrigin(): boolean { | ||||
|     return this.exposesHost() || this.exposesPath(); | ||||
|   } | ||||
| 
 | ||||
|   autoMark() { | ||||
|     if ( | ||||
|       this.classification == "history" || | ||||
|       ((this.source === "cookie" || | ||||
|         this.name.toLowerCase().includes("id") || | ||||
|         this.name.toLowerCase().includes("cookie") || | ||||
|         this.name.toLowerCase().includes("ga") || | ||||
|         this.name.toLowerCase().includes("ses") || | ||||
|         this.name.toLowerCase().includes("fb")) && | ||||
|         this.value.length > MIN_COOKIE_LENGTH_FOR_AUTO_MARK) | ||||
|     ) { | ||||
|       if ( | ||||
|         (this.request.shorthost.includes("google") || | ||||
|           this.request.shorthost.includes("youtube")) && | ||||
|         this.name == "CONSENT" | ||||
|       ) { | ||||
|         // this cookie contains "YES" and might distract the person looking at it into thinking i gave consent on the reported site
 | ||||
|         return; | ||||
|       } | ||||
|       this.mark(); | ||||
|     unmark() { | ||||
|         const had_been_marked_before = this.marked; | ||||
|         this.marked = false; | ||||
|         if (had_been_marked_before) { | ||||
|             this.emit('change'); | ||||
|         } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   exposesPath() { | ||||
|     return ( | ||||
|       this.request.originalPathname !== "/" && | ||||
|       [this.value, safeDecodeURIComponent(this.value)].some((haystack) => | ||||
|         haystack.includes(this.request.originalPathname) | ||||
|       ) | ||||
|     ); | ||||
|   } | ||||
|     toggleMark() { | ||||
|         if (this.marked) { | ||||
|             this.unmark(); | ||||
|         } else { | ||||
|             this.mark(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   exposesHost() { | ||||
|     return [this.value, safeDecodeURIComponent(this.value)].some((haystack) => | ||||
|       haystack.includes(getshorthost(this.request.origin)) | ||||
|     ); | ||||
|   } | ||||
|     private classify(): keyof typeof Classifications { | ||||
|         let result: keyof typeof Classifications; | ||||
|         if (this.exposesOrigin()) { | ||||
|             result = 'history'; | ||||
|         } else { | ||||
|             result = 'id'; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     isRelatedToID() { | ||||
|         return this.request.stolenData.some( | ||||
|             (entry) => entry.classification == 'id' | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     matchesHAREntry(har: HAREntry): boolean { | ||||
|         return this.request.matchesHAREntry(har); | ||||
|     } | ||||
| 
 | ||||
|     getValuePreview(key = ''): string { | ||||
|         const value = this.getParsedValue(key); | ||||
|         const str = | ||||
|             typeof value === 'object' && value[Symbol.for('originalString')] | ||||
|                 ? (value[Symbol.for('originalString')] as string) | ||||
|                 : value.toString(); | ||||
|         if (typeof value !== 'object' && this.classification == 'id') { | ||||
|             return maskString(value, 1 / 3, ID_PREVIEW_MAX_LENGTH); | ||||
|         } else if ( | ||||
|             typeof value === 'object' && | ||||
|             value[Symbol.for('originalString')] | ||||
|         ) { | ||||
|             return value[Symbol.for('originalString')] as string; | ||||
|         } else { | ||||
|             return str; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     getUniqueKey() { | ||||
|         return this.request.shorthost + ';' + this.name + ';' + this.value; | ||||
|     } | ||||
| 
 | ||||
|     exposesOrigin(): boolean { | ||||
|         return this.exposesHost() || this.exposesPath(); | ||||
|     } | ||||
| 
 | ||||
|     autoMark() { | ||||
|         if ( | ||||
|             this.classification == 'history' || | ||||
|             ((this.source === 'cookie' || | ||||
|                 this.name.toLowerCase().includes('id') || | ||||
|                 this.name.toLowerCase().includes('cookie') || | ||||
|                 this.name.toLowerCase().includes('ga') || | ||||
|                 this.name.toLowerCase().includes('ses') || | ||||
|                 this.name.toLowerCase().includes('fb')) && | ||||
|                 this.value.length > MIN_COOKIE_LENGTH_FOR_AUTO_MARK) | ||||
|         ) { | ||||
|             if ( | ||||
|                 (this.request.shorthost.includes('google') || | ||||
|                     this.request.shorthost.includes('youtube')) && | ||||
|                 this.name == 'CONSENT' | ||||
|             ) { | ||||
|                 // this cookie contains "YES" and might distract the person looking at it into thinking i gave consent on the reported site
 | ||||
|                 return; | ||||
|             } | ||||
|             this.mark(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     exposesPath() { | ||||
|         return ( | ||||
|             this.request.originalPathname !== '/' && | ||||
|             [this.value, safeDecodeURIComponent(this.value)].some((haystack) => | ||||
|                 haystack.includes(this.request.originalPathname) | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     exposesHost() { | ||||
|         return [this.value, safeDecodeURIComponent(this.value)].some( | ||||
|             (haystack) => haystack.includes(getshorthost(this.request.origin)) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user