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 +158 -0
- package/attack.js +555 -0
- package/bin/kryptheon-night.js +248 -0
- package/collision.js +309 -0
- package/connect.js +60 -0
- package/finding.js +552 -0
- package/intro.js +202 -0
- package/orphan.js +230 -0
- package/package.json +73 -0
- package/recheck.js +219 -0
- package/scan.js +569 -0
- package/schema.js +922 -0
- package/tamper.js +208 -0
- package/trouble.js +377 -0
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.
|