kryptheon-night 0.1.8 → 0.1.10
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/attack.js +46 -0
- package/bin/kryptheon-night.js +4 -1
- package/engine.sql +46 -1
- package/finding.js +344 -42
- package/package.json +4 -2
- package/recheck.js +45 -4
- package/scan.js +69 -6
- package/schema.js +1210 -1137
- package/store.js +15 -0
- package/tamper.js +20 -1
package/store.js
CHANGED
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
// history.jsonl was kryptheon-history.jsonl
|
|
15
15
|
// test-results/ was test-results/ (Playwright's screenshots of a failure)
|
|
16
16
|
// night-last.json was .kryptheon-last.json (kryptheon-night)
|
|
17
|
+
// code-findings.json the last frontend code read (kryptheon code)
|
|
18
|
+
// config.json which checks are switched on (the dashboard)
|
|
19
|
+
// snapshot.json a hash of each project file, to tell what changed
|
|
20
|
+
// changes.jsonl one line per change Kryptheon noticed, and what it ran
|
|
21
|
+
// fixes.jsonl every re-check of a finding someone tried to fix, and its verdict
|
|
17
22
|
//
|
|
18
23
|
// Only tests/ - the recordings the person made - stays in the project.
|
|
19
24
|
//
|
|
@@ -40,6 +45,11 @@ const FILES = {
|
|
|
40
45
|
history: 'history.jsonl',
|
|
41
46
|
testResults: 'test-results',
|
|
42
47
|
nightLast: 'night-last.json',
|
|
48
|
+
codeFindings: 'code-findings.json',
|
|
49
|
+
config: 'config.json',
|
|
50
|
+
snapshot: 'snapshot.json',
|
|
51
|
+
changes: 'changes.jsonl',
|
|
52
|
+
fixes: 'fixes.jsonl',
|
|
43
53
|
};
|
|
44
54
|
|
|
45
55
|
// What older versions wrote into the project, and where each one goes now.
|
|
@@ -122,6 +132,11 @@ function pathsIn(dir) {
|
|
|
122
132
|
history: path.join(dir, FILES.history),
|
|
123
133
|
testResults: path.join(dir, FILES.testResults),
|
|
124
134
|
nightLast: path.join(dir, FILES.nightLast),
|
|
135
|
+
codeFindings: path.join(dir, FILES.codeFindings),
|
|
136
|
+
config: path.join(dir, FILES.config),
|
|
137
|
+
snapshot: path.join(dir, FILES.snapshot),
|
|
138
|
+
changes: path.join(dir, FILES.changes),
|
|
139
|
+
fixes: path.join(dir, FILES.fixes),
|
|
125
140
|
};
|
|
126
141
|
}
|
|
127
142
|
|
package/tamper.js
CHANGED
|
@@ -131,7 +131,13 @@ async function tamper(client, schema, tables, seeded, policies) {
|
|
|
131
131
|
// Written under a name nobody has used, which is both what makes the
|
|
132
132
|
// row land at all and what makes it the right test: adding a row of
|
|
133
133
|
// your own is the feature, adding one under somebody else name is not.
|
|
134
|
-
|
|
134
|
+
//
|
|
135
|
+
// Unless the owner is a key into one of the app's own tables - provider_id
|
|
136
|
+
// -> profiles.id - where only the two seeded people exist. Found on a
|
|
137
|
+
// blind test: the nameless insert hit the foreign key, and "any provider
|
|
138
|
+
// can add appointments for any other" came back as not tested. The other
|
|
139
|
+
// seeded person is still somebody else's name to the signed-in caller.
|
|
140
|
+
const row = await attack.rowFor(client, schema, table, writeAsFor(table, owner, tables), 7, null, shapeFor.get(table.name));
|
|
135
141
|
const placeholders = row.values.map((_, i) => '$' + (i + 1));
|
|
136
142
|
const attempts = [
|
|
137
143
|
row.columns.length
|
|
@@ -186,6 +192,18 @@ async function tamper(client, schema, tables, seeded, policies) {
|
|
|
186
192
|
return { findings: findings, completed: completed, blocked: blocked };
|
|
187
193
|
}
|
|
188
194
|
|
|
195
|
+
/**
|
|
196
|
+
* Whose name an insert is written under: nobody's yet (USER_C), unless the
|
|
197
|
+
* owner column is a key into one of the app's own tables, where only the two
|
|
198
|
+
* seeded people exist - then the one who is not the signed-in caller.
|
|
199
|
+
*/
|
|
200
|
+
function writeAsFor(table, owner, tables) {
|
|
201
|
+
if (!owner) return attack.USER_C;
|
|
202
|
+
const key = attack.foreignKeys(table).find((k) => k.columns.length === 1 && k.columns[0] === owner);
|
|
203
|
+
const inApp = key && (tables || []).some((t) => t.name === key.refTable);
|
|
204
|
+
return inApp ? attack.USER_A : attack.USER_C;
|
|
205
|
+
}
|
|
206
|
+
|
|
189
207
|
/**
|
|
190
208
|
* A column an UPDATE can harmlessly set to itself.
|
|
191
209
|
*
|
|
@@ -204,5 +222,6 @@ module.exports = {
|
|
|
204
222
|
tryWrite: tryWrite,
|
|
205
223
|
whatHappened: whatHappened,
|
|
206
224
|
firstWritable: firstWritable,
|
|
225
|
+
writeAsFor: writeAsFor,
|
|
207
226
|
tamper: tamper,
|
|
208
227
|
};
|