wolverine-ai 1.0.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.
Files changed (79) hide show
  1. package/PLATFORM.md +442 -0
  2. package/README.md +475 -0
  3. package/SERVER_BEST_PRACTICES.md +62 -0
  4. package/TELEMETRY.md +108 -0
  5. package/bin/wolverine.js +95 -0
  6. package/examples/01-basic-typo.js +31 -0
  7. package/examples/02-multi-file/routes/users.js +15 -0
  8. package/examples/02-multi-file/server.js +25 -0
  9. package/examples/03-syntax-error.js +23 -0
  10. package/examples/04-secret-leak.js +14 -0
  11. package/examples/05-expired-key.js +27 -0
  12. package/examples/06-json-config/config.json +13 -0
  13. package/examples/06-json-config/server.js +28 -0
  14. package/examples/07-rate-limit-loop.js +11 -0
  15. package/examples/08-sandbox-escape.js +20 -0
  16. package/examples/buggy-server.js +39 -0
  17. package/examples/demos/01-basic-typo/index.js +20 -0
  18. package/examples/demos/01-basic-typo/routes/api.js +13 -0
  19. package/examples/demos/01-basic-typo/routes/health.js +4 -0
  20. package/examples/demos/02-multi-file/index.js +24 -0
  21. package/examples/demos/02-multi-file/routes/api.js +13 -0
  22. package/examples/demos/02-multi-file/routes/health.js +4 -0
  23. package/examples/demos/03-syntax-error/index.js +18 -0
  24. package/examples/demos/04-secret-leak/index.js +16 -0
  25. package/examples/demos/05-expired-key/index.js +21 -0
  26. package/examples/demos/06-json-config/config.json +9 -0
  27. package/examples/demos/06-json-config/index.js +20 -0
  28. package/examples/demos/07-null-crash/index.js +16 -0
  29. package/examples/run-demo.js +110 -0
  30. package/package.json +67 -0
  31. package/server/config/settings.json +62 -0
  32. package/server/index.js +33 -0
  33. package/server/routes/api.js +12 -0
  34. package/server/routes/health.js +16 -0
  35. package/server/routes/time.js +12 -0
  36. package/src/agent/agent-engine.js +727 -0
  37. package/src/agent/goal-loop.js +140 -0
  38. package/src/agent/research-agent.js +120 -0
  39. package/src/agent/sub-agents.js +176 -0
  40. package/src/backup/backup-manager.js +321 -0
  41. package/src/brain/brain.js +315 -0
  42. package/src/brain/embedder.js +131 -0
  43. package/src/brain/function-map.js +263 -0
  44. package/src/brain/vector-store.js +267 -0
  45. package/src/core/ai-client.js +387 -0
  46. package/src/core/cluster-manager.js +144 -0
  47. package/src/core/config.js +89 -0
  48. package/src/core/error-parser.js +87 -0
  49. package/src/core/health-monitor.js +129 -0
  50. package/src/core/models.js +132 -0
  51. package/src/core/patcher.js +55 -0
  52. package/src/core/runner.js +464 -0
  53. package/src/core/system-info.js +141 -0
  54. package/src/core/verifier.js +146 -0
  55. package/src/core/wolverine.js +290 -0
  56. package/src/dashboard/server.js +1332 -0
  57. package/src/index.js +94 -0
  58. package/src/logger/event-logger.js +237 -0
  59. package/src/logger/pricing.js +96 -0
  60. package/src/logger/repair-history.js +109 -0
  61. package/src/logger/token-tracker.js +277 -0
  62. package/src/mcp/mcp-client.js +224 -0
  63. package/src/mcp/mcp-registry.js +228 -0
  64. package/src/mcp/mcp-security.js +152 -0
  65. package/src/monitor/perf-monitor.js +300 -0
  66. package/src/monitor/process-monitor.js +231 -0
  67. package/src/monitor/route-prober.js +191 -0
  68. package/src/notifications/notifier.js +227 -0
  69. package/src/platform/heartbeat.js +93 -0
  70. package/src/platform/queue.js +53 -0
  71. package/src/platform/register.js +64 -0
  72. package/src/platform/telemetry.js +76 -0
  73. package/src/security/admin-auth.js +150 -0
  74. package/src/security/injection-detector.js +174 -0
  75. package/src/security/rate-limiter.js +152 -0
  76. package/src/security/sandbox.js +128 -0
  77. package/src/security/secret-redactor.js +217 -0
  78. package/src/skills/skill-registry.js +129 -0
  79. package/src/skills/sql.js +375 -0
@@ -0,0 +1,375 @@
1
+ /**
2
+ * SQL Skill — safe database interface + SQL injection prevention.
3
+ *
4
+ * Two roles:
5
+ * 1. DATABASE INTERFACE — provides a safe query API for the server
6
+ * - Parameterized queries only (no string concatenation ever)
7
+ * - Connection pooling
8
+ * - Supports SQLite (built-in), PostgreSQL, MySQL via adapters
9
+ * - Auto-close on process exit
10
+ *
11
+ * 2. INJECTION PREVENTION — middleware that scans all incoming requests
12
+ * - Detects SQL injection patterns in query params, body, headers
13
+ * - Blocks malicious requests before they reach route handlers
14
+ * - Logs attempts to wolverine event system
15
+ *
16
+ * Usage in server/:
17
+ * const { db, sqlGuard } = require("../src/skills/sql");
18
+ * app.use(sqlGuard()); // protect all routes
19
+ * const users = await db.all("SELECT * FROM users WHERE id = ?", [userId]);
20
+ */
21
+
22
+ const chalk = require("chalk");
23
+
24
+ // ── SQL Injection Detection ──────────────────────────────────────
25
+
26
+ const SQLI_PATTERNS = [
27
+ // Classic injection
28
+ /('\s*(OR|AND)\s+')/i,
29
+ /('\s*;\s*(DROP|DELETE|UPDATE|INSERT|ALTER|EXEC|UNION))/i,
30
+ /(--\s*$|#\s*$)/m,
31
+ /(\b(UNION)\s+(ALL\s+)?SELECT\b)/i,
32
+ // Tautologies
33
+ /('\s*=\s*')/,
34
+ /(\b1\s*=\s*1\b)/,
35
+ /(\bOR\s+1\s*=\s*1\b)/i,
36
+ // Stacked queries
37
+ /(;\s*(DROP|DELETE|TRUNCATE|ALTER|CREATE|INSERT|UPDATE)\s)/i,
38
+ // Comment-based bypass
39
+ /(\/\*.*\*\/)/,
40
+ // Hex/char encoding tricks
41
+ /(0x[0-9a-f]{4,})/i,
42
+ /(CHAR\s*\(\s*\d+\s*(,\s*\d+\s*)*\))/i,
43
+ // SLEEP/BENCHMARK (timing attacks)
44
+ /(SLEEP\s*\(\s*\d+\s*\))/i,
45
+ /(BENCHMARK\s*\()/i,
46
+ // Information schema probing
47
+ /(INFORMATION_SCHEMA)/i,
48
+ /(sys\.objects|sysobjects|syscolumns)/i,
49
+ // Load file / into outfile
50
+ /(LOAD_FILE|INTO\s+OUTFILE|INTO\s+DUMPFILE)/i,
51
+ ];
52
+
53
+ /**
54
+ * Check a string for SQL injection patterns.
55
+ * Returns { safe: boolean, patterns: string[] }
56
+ */
57
+ function scanForInjection(input) {
58
+ if (!input || typeof input !== "string") return { safe: true, patterns: [] };
59
+
60
+ const found = [];
61
+ for (const pattern of SQLI_PATTERNS) {
62
+ if (pattern.test(input)) {
63
+ found.push(pattern.source.slice(0, 40));
64
+ }
65
+ }
66
+
67
+ return { safe: found.length === 0, patterns: found };
68
+ }
69
+
70
+ /**
71
+ * Recursively scan an object (body, query, params) for injection.
72
+ */
73
+ function deepScan(obj, path = "") {
74
+ const results = [];
75
+ if (!obj) return results;
76
+
77
+ if (typeof obj === "string") {
78
+ const scan = scanForInjection(obj);
79
+ if (!scan.safe) results.push({ path: path || "value", patterns: scan.patterns, value: obj.slice(0, 100) });
80
+ return results;
81
+ }
82
+
83
+ if (Array.isArray(obj)) {
84
+ obj.forEach((item, i) => results.push(...deepScan(item, `${path}[${i}]`)));
85
+ return results;
86
+ }
87
+
88
+ if (typeof obj === "object") {
89
+ for (const [key, val] of Object.entries(obj)) {
90
+ results.push(...deepScan(val, path ? `${path}.${key}` : key));
91
+ }
92
+ }
93
+
94
+ return results;
95
+ }
96
+
97
+ /**
98
+ * Express middleware — blocks requests with SQL injection patterns.
99
+ *
100
+ * @param {object} options
101
+ * @param {object} options.logger — wolverine EventLogger (optional)
102
+ * @param {boolean} options.blockMode — true = block request, false = log only (default: true)
103
+ */
104
+ function sqlGuard(options = {}) {
105
+ const logger = options.logger || null;
106
+ const blockMode = options.blockMode !== false;
107
+
108
+ return (req, res, next) => {
109
+ const threats = [];
110
+
111
+ // Scan query params
112
+ threats.push(...deepScan(req.query, "query"));
113
+
114
+ // Scan body
115
+ if (req.body) threats.push(...deepScan(req.body, "body"));
116
+
117
+ // Scan URL params
118
+ if (req.params) threats.push(...deepScan(req.params, "params"));
119
+
120
+ // Scan select headers (user-agent, referer, cookie values)
121
+ const suspectHeaders = ["user-agent", "referer", "x-forwarded-for"];
122
+ for (const h of suspectHeaders) {
123
+ if (req.headers[h]) threats.push(...deepScan(req.headers[h], `header.${h}`));
124
+ }
125
+
126
+ if (threats.length === 0) {
127
+ return next();
128
+ }
129
+
130
+ // SQL injection detected
131
+ const summary = threats.map(t => `${t.path}: ${t.patterns.join(", ")}`).join(" | ");
132
+ console.log(chalk.red(` 🛡️ SQL INJECTION BLOCKED: ${req.method} ${req.path} — ${summary}`));
133
+
134
+ if (logger) {
135
+ logger.critical("security.sqli_blocked", `SQL injection blocked: ${req.method} ${req.path}`, {
136
+ method: req.method,
137
+ path: req.path,
138
+ threats: threats.map(t => ({ path: t.path, value: t.value })),
139
+ ip: req.ip || req.socket.remoteAddress,
140
+ });
141
+ }
142
+
143
+ if (blockMode) {
144
+ res.status(403).json({ error: "Forbidden", message: "Potentially malicious input detected." });
145
+ return;
146
+ }
147
+
148
+ // Log-only mode — let request through but flag it
149
+ req._sqliWarning = threats;
150
+ next();
151
+ };
152
+ }
153
+
154
+ // ── Safe Database Interface ──────────────────────────────────────
155
+
156
+ /**
157
+ * Cluster-safe database — zero waits, zero blocking.
158
+ *
159
+ * Architecture:
160
+ * - Reads: every worker has its own read-only connection (concurrent, no locks)
161
+ * - Writes: single dedicated write connection with a synchronous FIFO queue
162
+ *
163
+ * How the write queue works:
164
+ * - Writes are synchronous in better-sqlite3 (microseconds each)
165
+ * - Queue is just an array that drains in a microtask
166
+ * - No busy_timeout, no delays, no IPC — writes are fast, they just can't overlap
167
+ * - Queue forms naturally under load, drains instantly when the current write finishes
168
+ *
169
+ * SQLite specifics:
170
+ * - WAL mode: readers never block writers, writers never block readers
171
+ * - No busy_timeout needed — only one connection writes, so no contention
172
+ * - NORMAL sync: safe with WAL, 1 fsync per transaction instead of 2
173
+ */
174
+ class SafeDB {
175
+ constructor(options = {}) {
176
+ this.type = options.type || "sqlite";
177
+ this.path = options.path || ":memory:";
178
+ this._reader = null; // read-only connection (concurrent)
179
+ this._writer = null; // single write connection (serialized)
180
+ this._closed = false;
181
+ this._queue = []; // pending write operations
182
+ this._draining = false;
183
+ }
184
+
185
+ async connect() {
186
+ if (this.type === "sqlite") {
187
+ try {
188
+ const Database = require("better-sqlite3");
189
+
190
+ // Read connection — used by get() and all()
191
+ this._reader = new Database(this.path, { readonly: false });
192
+ this._reader.pragma("journal_mode = WAL");
193
+ this._reader.pragma("foreign_keys = ON");
194
+ this._reader.pragma("synchronous = NORMAL");
195
+ this._reader.pragma("cache_size = -20000");
196
+
197
+ // Write connection — only used by run(), exec(), transaction()
198
+ // Separate connection means reads never wait for writes
199
+ this._writer = new Database(this.path);
200
+ this._writer.pragma("journal_mode = WAL");
201
+ this._writer.pragma("foreign_keys = ON");
202
+ this._writer.pragma("synchronous = NORMAL");
203
+
204
+ } catch (err) {
205
+ if (err.code === "MODULE_NOT_FOUND") {
206
+ throw new Error("Install better-sqlite3: npm install better-sqlite3");
207
+ }
208
+ throw err;
209
+ }
210
+ } else if (this.type === "custom") {
211
+ // User manages their own connection
212
+ } else {
213
+ throw new Error(`Unsupported DB type: ${this.type}. Use "sqlite" or pass a custom driver.`);
214
+ }
215
+
216
+ process.on("exit", () => this.close());
217
+ }
218
+
219
+ /**
220
+ * Write query (INSERT, UPDATE, DELETE, CREATE).
221
+ * Queued and executed in order. Returns a promise that resolves with the result.
222
+ */
223
+ run(sql, params = []) {
224
+ this._assertOpen();
225
+ this._assertSafe(sql);
226
+ return this._enqueueWrite(() => this._writer.prepare(sql).run(...params));
227
+ }
228
+
229
+ /**
230
+ * Read one row. Direct on the read connection — never waits.
231
+ */
232
+ get(sql, params = []) {
233
+ this._assertOpen();
234
+ this._assertSafe(sql);
235
+ return this._reader.prepare(sql).get(...params);
236
+ }
237
+
238
+ /**
239
+ * Read all rows. Direct on the read connection — never waits.
240
+ */
241
+ all(sql, params = []) {
242
+ this._assertOpen();
243
+ this._assertSafe(sql);
244
+ return this._reader.prepare(sql).all(...params);
245
+ }
246
+
247
+ /**
248
+ * Raw SQL execution (migrations/schema). Queued through the writer.
249
+ */
250
+ exec(sql) {
251
+ this._assertOpen();
252
+ return this._enqueueWrite(() => this._writer.exec(sql));
253
+ }
254
+
255
+ /**
256
+ * Atomic transaction. All writes inside fn run as one unit.
257
+ * Queued as a single operation — no other writes can interleave.
258
+ */
259
+ transaction(fn) {
260
+ this._assertOpen();
261
+ return this._enqueueWrite(() => {
262
+ const txn = this._writer.transaction(() => fn(this._writerProxy()));
263
+ return txn();
264
+ });
265
+ }
266
+
267
+ close() {
268
+ if (this._closed) return;
269
+ this._closed = true;
270
+ try {
271
+ if (this._writer) {
272
+ try { this._writer.pragma("wal_checkpoint(TRUNCATE)"); } catch {}
273
+ this._writer.close();
274
+ }
275
+ if (this._reader) this._reader.close();
276
+ } catch {}
277
+ }
278
+
279
+ _assertOpen() {
280
+ if (this._closed || (!this._reader && !this._writer)) {
281
+ throw new Error("Database not connected. Call db.connect() first.");
282
+ }
283
+ }
284
+
285
+ _assertSafe(sql) {
286
+ if (/'\s*\+\s*/.test(sql) || /`\$\{/.test(sql)) {
287
+ throw new Error("UNSAFE: SQL appears to use string concatenation. Use parameterized queries (?) instead.");
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Write queue — FIFO, no delays, drains synchronously.
293
+ * Each write is microseconds. Queue only forms under concurrent write pressure.
294
+ * Drains completely in a single microtask when the current write finishes.
295
+ */
296
+ _enqueueWrite(fn) {
297
+ return new Promise((resolve, reject) => {
298
+ this._queue.push({ fn, resolve, reject });
299
+ if (!this._draining) this._drain();
300
+ });
301
+ }
302
+
303
+ _drain() {
304
+ this._draining = true;
305
+ while (this._queue.length > 0) {
306
+ const { fn, resolve, reject } = this._queue.shift();
307
+ try {
308
+ resolve(fn());
309
+ } catch (err) {
310
+ reject(err);
311
+ }
312
+ }
313
+ this._draining = false;
314
+ }
315
+
316
+ /**
317
+ * Proxy for use inside transactions — writes go directly to writer (already locked).
318
+ */
319
+ _writerProxy() {
320
+ const writer = this._writer;
321
+ return {
322
+ run: (sql, params = []) => writer.prepare(sql).run(...params),
323
+ exec: (sql) => writer.exec(sql),
324
+ get: (sql, params = []) => writer.prepare(sql).get(...params),
325
+ all: (sql, params = []) => writer.prepare(sql).all(...params),
326
+ };
327
+ }
328
+ }
329
+
330
+ // ── Skill Metadata (for SkillRegistry discovery) ──
331
+
332
+ const SKILL_NAME = "sql";
333
+ const SKILL_DESCRIPTION = "SQL database interface with injection prevention. Provides sqlGuard() middleware to block SQL injection on all endpoints, and SafeDB class for parameterized-only database queries.";
334
+ const SKILL_KEYWORDS = ["sql", "database", "db", "query", "injection", "sqlite", "postgres", "mysql", "select", "insert", "update", "delete", "table", "schema", "migration", "parameterized"];
335
+ const SKILL_USAGE = `// Protect all routes from SQL injection
336
+ const { sqlGuard } = require("../src/skills/sql");
337
+ app.use(sqlGuard({ logger: wolverineLogger }));
338
+
339
+ // Cluster-safe database (each worker gets its own connection)
340
+ const { SafeDB } = require("../src/skills/sql");
341
+ const db = new SafeDB({ type: "sqlite", path: "./server/data.db" });
342
+ await db.connect(); // WAL mode, busy_timeout=5s, write serialization
343
+
344
+ // Reads (concurrent across workers)
345
+ const users = db.all("SELECT * FROM users WHERE role = ?", ["admin"]);
346
+
347
+ // Writes (serialized — no corruption)
348
+ db.run("INSERT INTO users (name, role) VALUES (?, ?)", ["Alice", "admin"]);
349
+
350
+ // Batch writes (atomic transaction, single lock)
351
+ db.transaction((tx) => {
352
+ tx.run("INSERT INTO orders (user_id, total) VALUES (?, ?)", [1, 99.99]);
353
+ tx.run("UPDATE users SET order_count = order_count + 1 WHERE id = ?", [1]);
354
+ });`;
355
+
356
+ // ── Exports ──
357
+
358
+ module.exports = {
359
+ // Skill metadata
360
+ SKILL_NAME,
361
+ SKILL_DESCRIPTION,
362
+ SKILL_KEYWORDS,
363
+ SKILL_USAGE,
364
+
365
+ // Middleware
366
+ sqlGuard,
367
+ scanForInjection,
368
+ deepScan,
369
+
370
+ // Database
371
+ SafeDB,
372
+
373
+ // Pattern list
374
+ SQLI_PATTERNS,
375
+ };