33
44import type { AddressList , HostList } from './types.ts' ;
55
6- import { u8aEq } from 'https://deno.land/x/polkadot@0.2.23 /util/mod.ts' ;
7- import { decodeAddress } from 'https://deno.land/x/polkadot@0.2.23 /util-crypto/mod.ts' ;
6+ import { u8aEq } from 'https://deno.land/x/polkadot/util/mod.ts' ;
7+ import { decodeAddress } from 'https://deno.land/x/polkadot/util-crypto/mod.ts' ;
88
99import { fetchJson } from './fetch.ts' ;
1010
1111export { packageInfo } from './packageInfo.ts' ;
1212
13+ interface Cache < T > {
14+ end : number ;
15+ list : T ;
16+ }
17+
18+ interface CacheAddrList extends Cache < AddressList > {
19+ u8a : [ string , Uint8Array [ ] ] [ ] ;
20+ }
21+
22+ interface CacheHostList extends Cache < HostList > {
23+ parts : string [ ] [ ] ;
24+ }
25+
1326// Equivalent to https://raw.githubusercontent.com/polkadot-js/phishing/master/{address,all}.json
14- const ADDRESS_JSON = 'https://polkadot.js.org/phishing/address.json ' ;
15- const ALL_JSON = 'https://polkadot.js.org/phishing/all. json' ;
27+ const PHISHING = 'https://polkadot.js.org/phishing' ;
28+ const ADDRESS_JSON = ` ${ PHISHING } /address. json` ;
1629// 45 minutes cache refresh
1730const CACHE_TIMEOUT = 45 * 60 * 1000 ;
1831
19- let cacheAddrEnd = 0 ;
20- let cacheAddrList : AddressList | null = null ;
21- let cacheAddrU8a : [ string , Uint8Array [ ] ] [ ] | null = null ;
22- let cacheHostEnd = 0 ;
23- let cacheHostList : HostList | null = null ;
24-
25- // gets the host-only part for a host
26- function extractHost ( path : string ) : string {
27- return path
28- . replace ( / h t t p s : \/ \/ | h t t p : \/ \/ | w s s : \/ \/ | w s : \/ \/ / , '' )
29- . split ( '/' ) [ 0 ] ;
32+ const cacheAddr : CacheAddrList = {
33+ end : 0 ,
34+ list : { } ,
35+ u8a : [ ]
36+ } ;
37+ const cacheHost : Record < string , CacheHostList > = { } ;
38+
39+ function splitHostParts ( host : string ) : string [ ] {
40+ return host
41+ // split domain
42+ . split ( '.' )
43+ // reverse order
44+ . reverse ( ) ;
3045}
3146
32- // logs an error in a consistent format
33- function log ( error : unknown , check : string ) : void {
34- console . warn ( `Error checking ${ check } , assuming non-phishing` , ( error as Error ) . message ) ;
47+ function extractHostParts ( host : string ) : string [ ] {
48+ return splitHostParts (
49+ host
50+ // remove protocol
51+ . replace ( / h t t p s : \/ \/ | h t t p : \/ \/ | w s s : \/ \/ | w s : \/ \/ / , '' )
52+ // get the domain-only part
53+ . split ( '/' ) [ 0 ]
54+ ) ;
3555}
3656
37- /**
38- * Retrieve a list of known phishing addresses
39- */
40- export async function retrieveAddrList ( allowCached = true ) : Promise < AddressList > {
57+ async function retrieveAddrCache ( allowCached = true ) : Promise < CacheAddrList > {
4158 const now = Date . now ( ) ;
4259
43- return ( allowCached && cacheAddrList && ( now < cacheAddrEnd ) )
44- ? cacheAddrList
45- : fetchJson < AddressList > ( ADDRESS_JSON ) . then ( ( list ) => {
46- cacheAddrEnd = now + CACHE_TIMEOUT ;
47- cacheAddrList = list ;
60+ if ( allowCached && ( now < cacheAddr . end ) ) {
61+ return cacheAddr ;
62+ }
63+
64+ const list = await fetchJson < AddressList > ( ADDRESS_JSON ) ;
65+
66+ cacheAddr . end = now + CACHE_TIMEOUT ;
67+ cacheAddr . list = list ;
68+ cacheAddr . u8a = Object . entries ( list ) . map ( ( [ key , addresses ] ) : [ string , Uint8Array [ ] ] =>
69+ [ key , addresses . map ( ( a ) => decodeAddress ( a ) ) ]
70+ ) ;
71+
72+ return cacheAddr ;
73+ }
74+
75+ async function retrieveHostCache ( allowCached = true , root = '*' ) : Promise < CacheHostList > {
76+ const now = Date . now ( ) ;
77+
78+ if ( allowCached && cacheHost [ root ] && ( now < cacheHost [ root ] . end ) ) {
79+ return cacheHost [ root ] ;
80+ }
81+
82+ let list : HostList ;
83+
84+ try {
85+ list = root === '*'
86+ ? await fetchJson < HostList > ( `${ PHISHING } /all.json` )
87+ : {
88+ allow : [ ] ,
89+ deny : await fetchJson < string [ ] > ( `${ PHISHING } /all/${ root } /all.json` )
90+ } ;
91+ } catch {
92+ list = { allow : [ ] , deny : [ ] } ;
93+ }
94+
95+ cacheHost [ root ] = {
96+ end : now + CACHE_TIMEOUT ,
97+ list,
98+ parts : list . deny . map ( ( h ) => splitHostParts ( h ) )
99+ } ;
48100
49- return list ;
50- } ) ;
101+ return cacheHost [ root ] ;
102+ }
103+
104+ function checkHostParts ( items : CacheHostList [ 'parts' ] , hostParts : string [ ] ) : boolean {
105+ return items . some ( ( parts ) =>
106+ // out list entry needs to have the same (or less) parts
107+ ( parts . length <= hostParts . length ) &&
108+ // ensure each section matches
109+ parts . every ( ( part , index ) => hostParts [ index ] === part )
110+ ) ;
51111}
52112
53113/**
54- * Retrieve a list of known phishing addresses in raw Uint8Array format
114+ * Retrieve a list of known phishing addresses
115+ *
116+ * @deprecated While not due for removal, it is suggested to rather use the
117+ * checkAddress function (which is more optimal overall)
55118 */
56- async function retrieveAddrU8a ( allowCached = true ) : Promise < [ string , Uint8Array [ ] ] [ ] > {
57- const now = Date . now ( ) ;
119+ export async function retrieveAddrList ( allowCached = true ) : Promise < AddressList > {
120+ const cache = await retrieveAddrCache ( allowCached ) ;
58121
59- return ( allowCached && cacheAddrU8a && ( now < cacheAddrEnd ) )
60- ? cacheAddrU8a
61- : retrieveAddrList ( allowCached ) . then ( ( all ) => {
62- cacheAddrU8a = Object
63- . entries ( all )
64- . map ( ( [ key , addresses ] ) : [ string , Uint8Array [ ] ] =>
65- [ key , addresses . map ( ( a ) => decodeAddress ( a ) ) ]
66- ) ;
67-
68- return cacheAddrU8a ;
69- } ) ;
122+ return cache . list ;
70123}
71124
72125/**
73126 * Retrieve allow/deny from our list provider
127+ *
128+ * @deprecated While not due for removal, it is suggested to rather use the
129+ * checkIfDenied function (which is more optimal overall)
74130 */
75- export async function retrieveHostList ( allowCached = true ) : Promise < HostList > {
76- const now = Date . now ( ) ;
77-
78- return ( allowCached && cacheHostList && ( now < cacheHostEnd ) )
79- ? cacheHostList
80- : fetchJson < HostList > ( ALL_JSON ) . then ( ( list ) => {
81- cacheHostEnd = now + CACHE_TIMEOUT ;
82- cacheHostList = list ;
131+ export async function retrieveHostList ( allowCached = true , root = '*' ) : Promise < HostList > {
132+ const cache = await retrieveHostCache ( allowCached , root ) ;
83133
84- return list ;
85- } ) ;
134+ return cache . list ;
86135}
87136
88137/**
89138 * Checks a host to see if it appears in the provided list
139+ *
140+ * @deprecated While not due for removal, it is suggested to rather use the
141+ * checkIfDenied function (which is more optimal overall)
90142 */
91- export function checkHost ( items : string [ ] , host : string ) : boolean {
92- const hostParts = extractHost ( host ) . split ( '.' ) . reverse ( ) ;
93-
94- return items . some ( ( item ) : boolean => {
95- const checkParts = item . split ( '.' ) . reverse ( ) ;
96-
97- // first we need to ensure it has less or equal parts to our source
98- if ( checkParts . length > hostParts . length ) {
99- return false ;
100- }
101-
102- // ensure each section matches
103- return checkParts . every ( ( part , index ) => hostParts [ index ] === part ) ;
104- } ) ;
143+ export function checkHost ( list : string [ ] , host : string ) : boolean {
144+ return checkHostParts ( list . map ( ( h ) => splitHostParts ( h ) ) , extractHostParts ( host ) ) ;
105145}
106146
107147/**
@@ -111,13 +151,13 @@ export function checkHost (items: string[], host: string): boolean {
111151export async function checkAddress ( address : string | Uint8Array , allowCached = true ) : Promise < string | null > {
112152 try {
113153 const u8a = decodeAddress ( address ) ;
114- const all = await retrieveAddrU8a ( allowCached ) ;
115- const entry = all . find ( ( [ , all ] ) => all . some ( ( a ) => u8aEq ( a , u8a ) ) ) || [ null ] ;
116-
117- return entry [ 0 ] ;
118- } catch ( error ) {
119- log ( error , 'address' ) ;
154+ const cache = await retrieveAddrCache ( allowCached ) ;
155+ const entry = cache . u8a . find ( ( [ , u8as ] ) =>
156+ u8as . some ( ( a ) => u8aEq ( a , u8a ) )
157+ ) ;
120158
159+ return ( entry && entry [ 0 ] ) || null ;
160+ } catch {
121161 return null ;
122162 }
123163}
@@ -128,12 +168,11 @@ export async function checkAddress (address: string | Uint8Array, allowCached =
128168 */
129169export async function checkIfDenied ( host : string , allowCached = true ) : Promise < boolean > {
130170 try {
131- const { deny } = await retrieveHostList ( allowCached ) ;
132-
133- return checkHost ( deny , host ) ;
134- } catch ( error ) {
135- log ( error , host ) ;
171+ const hostParts = extractHostParts ( host ) ;
172+ const cache = await retrieveHostCache ( allowCached , hostParts [ 0 ] ) ;
136173
174+ return checkHostParts ( cache . parts , hostParts ) ;
175+ } catch {
137176 return false ;
138177 }
139178}
0 commit comments