forked from riccardobl/nwcprovider
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparanoia.py
170 lines (134 loc) · 4.18 KB
/
paranoia.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# Run-time hardening to detect unexpected inputs at the edges
from loguru import logger
ENABLE_HARDENING = True
def panic(reason: str):
if not ENABLE_HARDENING:
return
logger.error(f"hardening: {reason}")
raise ValueError(f"hardening: {reason}")
# Throw if string contains any non-printable characters
def assert_printable(v: str):
if not ENABLE_HARDENING:
return
if not isinstance(v, str):
panic("not a string " + str(v))
if not v.isprintable():
panic("string contains non-printable characters")
# Check if number is valid int and not NaN
def assert_valid_int(v: int):
if not ENABLE_HARDENING:
return
if not isinstance(v, int):
panic("number is not a valid int")
# Check if number is valid positive int
def assert_valid_positive_int(v: int):
if not ENABLE_HARDENING:
return
assert_valid_int(v)
if v < 0:
panic("number is not positive")
# Check if number is a valid sats amount
def assert_valid_sats(v: int):
if not ENABLE_HARDENING:
return
assert_valid_positive_int(v)
max_sats_value = 10_000_000
if v >= max_sats_value:
panic("sats amount looks too high")
# Check if number is a valid msats amount
def assert_valid_msats(v: int):
if not ENABLE_HARDENING:
return
assert_valid_positive_int(v)
max_msats_value = 10_000_000 * 1000
if v >= max_msats_value:
panic("msats amount looks too high")
# Check if string is a valid sha256 hash
def assert_valid_sha256(v: str):
if not ENABLE_HARDENING:
return
assert_printable(v)
if len(v) != 64 or not all(c in "0123456789abcdef" for c in v):
panic("string is not a valid sha256 hash")
# Check if value is an hash of an unexpected input (eg. empty strings, booleans etc)
def assert_no_badhash(v: str):
bad_hashes = [
# empty string
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
# 1 space string
"36a9e7f1c95b82ffb99743e0c5c4ce95d83c9a430aac59f84ef3cbfab6145068",
# None
"c1c4b7fbd3e146bb14ec6258e5231c1ec703590721ff1e321b179a62b5857c9c",
# True
"cdca0b9bb2325fc8ed7eba7734a3a1f876d919221399b6587ae7d26305adee9d",
# False
"f9e08f8b038b1b401497f17da3adc120667ac742bf035657869a6ca1cd180e69",
]
if v in bad_hashes:
panic("bad hash detected")
# Check if valid nostr pubkey
def assert_valid_pubkey(v: str):
if not ENABLE_HARDENING:
return
assert_valid_sha256(v)
assert_no_badhash(v)
# Check if valid wallet id
def assert_valid_wallet_id(v: str):
if not ENABLE_HARDENING:
return
assert_printable(v)
if not v.isalnum():
panic("string is not a valid wallet id")
# Check if valid timestamp in seconds
def assert_valid_timestamp_seconds(v: int):
if not ENABLE_HARDENING:
return
assert_valid_positive_int(v)
if v > 2**31:
panic("timestamp is too high")
# Check if valid expiration in seconds
def assert_valid_expiration_seconds(v: int):
if not ENABLE_HARDENING:
return
assert_valid_int(v)
if v == -1:
return
if v < 0:
panic("expiration is invalid")
if v > 2**31:
panic("expiration is too high")
# Check if string is within sane parameters
def assert_sane_string(v: str):
if not ENABLE_HARDENING:
return
assert_printable(v)
if len(v) > 1024:
panic("string is too long")
# Check if string is a non-empty string
def assert_non_empty_string(v: str):
if not ENABLE_HARDENING:
return
assert_printable(v)
if len(v.strip()) == 0:
panic("string is empty")
# Assert valid json
def assert_valid_json(v: str):
if not ENABLE_HARDENING:
return
assert_non_empty_string(v)
try:
import json
json.loads(v)
except Exception as e:
panic("string is not valid json " + str(e))
# Check if string is a valid bolt11 invoice
def assert_valid_bolt11(invoice: str):
if not ENABLE_HARDENING:
return
assert_printable(invoice)
# Check if boolean
def assert_boolean(v: bool):
if not ENABLE_HARDENING:
return
if not isinstance(v, bool):
panic("not a boolean")