kryptheon-night 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # Kryptheon Night Shift
2
+
3
+ Attacks a copy of your database and tells you, in plain English, what got in.
4
+
5
+ ```
6
+ npx kryptheon-night
7
+ ```
8
+
9
+ That is the whole thing. No account, no signup, no config file. It asks you
10
+ for one line from your Supabase dashboard, tells you exactly what it is about
11
+ to do, waits for you to say yes, and then takes two or three minutes.
12
+
13
+ **It never touches your live app.** It reads the *shape* of your database —
14
+ table names, columns, and the rules about who may see what — rebuilds that
15
+ shape in a temporary space, puts two made-up people in it, and attacks *that*.
16
+ Not one of your rows is read, changed or copied. The temporary space is
17
+ deleted when it finishes, and also if it crashes.
18
+
19
+ Your connection string stays on your computer. Nothing is uploaded anywhere.
20
+ There is no server on our side to upload it to.
21
+
22
+ ## What you need
23
+
24
+ One line from Supabase, called the connection string.
25
+
26
+ **Supabase** — supabase.com/dashboard → your project → the gear icon
27
+ (Project Settings) at the bottom left → Database → scroll to
28
+ "Connection string" → the **URI** tab → Copy. Then replace `[YOUR-PASSWORD]`
29
+ in it with your database password, which is on that same page.
30
+
31
+ **Lovable, Bolt, v0** — your app is running on Supabase underneath. Open the
32
+ Supabase project it made for you and follow the lines above.
33
+
34
+ **Neon** — console.neon.tech → your project → Connection Details.
35
+
36
+ If you paste the wrong thing — the project URL, an API key, the line with
37
+ `[YOUR-PASSWORD]` still in it — it says so and tells you where the right one
38
+ is. It does not just fail.
39
+
40
+ ## What you get back
41
+
42
+ Four questions, asked against the copy:
43
+
44
+ | attack | the question |
45
+ |---|---|
46
+ | **Impersonation** | can a stranger, or another customer, read this? |
47
+ | **Tampering** | can a stranger add, change or delete your data? |
48
+ | **Collision** | can the same thing exist twice? |
49
+ | **Interruption** | can a half-finished write survive? |
50
+
51
+ A problem reads like this:
52
+
53
+ CRITICAL customers
54
+
55
+ Your customers table can be read by anyone.
56
+
57
+ Anyone on the internet, without logging in and without an account, can
58
+ read this table. I did it myself just now and got back 2 rows,
59
+ including email addresses, phone numbers and names.
60
+
61
+ The table has row level security switched on, so it looks protected,
62
+ but the rule attached to it allows every request. That is why nothing
63
+ in your dashboard flags it.
64
+
65
+ Paste this into Lovable, Claude or Cursor:
66
+
67
+ My app has a security problem.
68
+ ...
69
+
70
+ And every report ends with **what was not tested, and why** — because an
71
+ attack that never ran comes back looking exactly like an attack that was
72
+ refused, and only one of those is good news.
73
+
74
+ Nothing found is never reported as "you are safe". It is reported as *these
75
+ attacks, this time, lost*.
76
+
77
+ ## Prove the fix worked
78
+
79
+ Paste the fix into Lovable or Cursor, let it deploy, then:
80
+
81
+ ```
82
+ npx kryptheon-night --recheck
83
+ ```
84
+
85
+ It runs the same attacks again and compares. A problem that *disappeared* is
86
+ not the same as a problem that was *fixed* — if a table could not be tested
87
+ this time, or is no longer there, it says so rather than crediting you with a
88
+ fix. Close everything, with nothing left untested, and it hands out a badge.
89
+
90
+ ## Three answers, three exit codes
91
+
92
+ | exit | meaning |
93
+ |---|---|
94
+ | `0` | every attack that ran, lost |
95
+ | `1` | something got through |
96
+ | `2` | it could not run — wrong string, wrong schema name, nothing to attack |
97
+
98
+ ## Running it unattended
99
+
100
+ ```
101
+ KN_DATABASE_URL="postgresql://..." npx kryptheon-night --yes
102
+ ```
103
+
104
+ `--yes` skips the "may I?" question, and is only for scripts. With no
105
+ keyboard attached and no `--yes`, it stops rather than connecting: the consent
106
+ screen is not worth printing if it is not honoured.
107
+
108
+ Other options, none of them necessary:
109
+
110
+ --schema NAME the part of the database your app lives in. Leave it
111
+ out; it is "public" for almost everyone
112
+ --recheck run the same attacks again and say what is really fixed
113
+ --help
114
+
115
+ ## Deliberately not reported
116
+
117
+ The **lost update** — two withdrawals of 100 from a balance of 100 that both
118
+ go through. Every Postgres database behaves that way unless the app asks it
119
+ not to, so whether yours is affected depends on code this tool never sees.
120
+ Flagging it would mean flagging every app with a number in it.
121
+
122
+ ## The promise, and how it is kept
123
+
124
+ > We never touch your live app.
125
+
126
+ Not a policy — a structural guard. `writeSchema` refuses to run any statement
127
+ that does not name the throwaway copy, so nothing outside it can be written
128
+ even by accident. `untouched.check.js` photographs every function, privilege,
129
+ table, column, policy, role and row in the database, runs a real scan, and
130
+ fails if a single one changed.
131
+
132
+ It found two real violations the day it was written: the copy builder was
133
+ replacing the customer's own `auth.uid()` function, and opening their `auth`
134
+ schema to `anon`. Both on a live database, while every other check was green.
135
+
136
+ ## Working on it
137
+
138
+ ```
139
+ npm install
140
+ npm run check:dry # no database needed
141
+ KN_DATABASE_URL="postgresql://..." npm run check # all of it
142
+ ```
143
+
144
+ `check:dry` is the packaging, the wording of every failure, the consent
145
+ screen, the report and the re-check — sixty checks that need nothing but
146
+ Node. `npm run check` adds fourteen suites that each run against a real
147
+ Postgres.
148
+
149
+ See [CHECKING.md](CHECKING.md) — what each suite is guarding, the bugs they
150
+ caught, and why a check that leaves the database different from how it found
151
+ it is not a check.
152
+
153
+ `scan.js` is the same engine with the arguments on the command line
154
+ (`node scan.js "<connection string>" public`). That is the door the checks use
155
+ and it has not changed.
156
+
157
+ If your network blocks outbound 5432, set `KN_PRELOAD` to a module that swaps
158
+ the driver for one reaching Postgres over 443.