shieldx/scripts/setup-db.ts
Rene Fichtmueller a3793a1357 feat: ShieldX v0.1.0 — Self-Evolving LLM Prompt Injection Defense
10-layer defense pipeline with kill chain mapping, self-healing,
self-learning, and compliance reporting. Local-first, zero cloud deps.

- 72 detection rules across 7 kill chain phases
- 294 unit tests, 500+ attack corpus samples
- Management dashboard (Next.js 15, 10 pages)
- Automated resistance testing (2x daily, 31 probes)
- MITRE ATLAS, OWASP LLM Top 10, EU AI Act compliance
- Integrations: Next.js middleware, Ollama, n8n
- PostgreSQL 17 + pgvector for persistent learning
2026-03-27 15:07:27 +13:00

122 lines
3.2 KiB
TypeScript

/**
* ShieldX Database Migration Runner
*
* Reads DATABASE_URL from environment, connects to PostgreSQL,
* and runs all SQL migration files in order.
*
* Usage:
* npm run db:migrate # Run pending migrations
* npm run db:migrate -- --reset # Drop all shieldx_* tables, then re-run
*/
import { readFileSync, readdirSync } from 'node:fs';
import { join } from 'node:path';
import { Client } from 'pg';
const MIGRATIONS_DIR = join(__dirname, '..', 'src', 'learning', 'migrations');
const TABLE_DROP_ORDER = [
'shieldx_drift_reports',
'shieldx_conversation_turns',
'shieldx_conversation_state',
'shieldx_attack_edges',
'shieldx_attack_nodes',
'shieldx_embeddings',
'shieldx_feedback',
'shieldx_incidents',
'shieldx_sessions',
'shieldx_patterns',
];
function getMigrationFiles(): readonly string[] {
const files = readdirSync(MIGRATIONS_DIR)
.filter((f) => f.endsWith('.sql'))
.sort();
if (files.length === 0) {
throw new Error(`No .sql files found in ${MIGRATIONS_DIR}`);
}
return files;
}
async function createClient(): Promise<Client> {
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error(
'DATABASE_URL environment variable is required.\n' +
'Example: DATABASE_URL=postgresql://user:pass@localhost:5432/shieldx',
);
}
const client = new Client({ connectionString: databaseUrl });
await client.connect();
return client;
}
async function dropAllTables(client: Client): Promise<void> {
console.log('\n--- RESET MODE: Dropping all shieldx_* tables ---\n');
for (const table of TABLE_DROP_ORDER) {
try {
await client.query(`DROP TABLE IF EXISTS ${table} CASCADE`);
console.log(` Dropped: ${table}`);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
console.warn(` Warning dropping ${table}: ${message}`);
}
}
console.log('\n--- All tables dropped ---\n');
}
async function runMigration(
client: Client,
filename: string,
): Promise<void> {
const filepath = join(MIGRATIONS_DIR, filename);
const sql = readFileSync(filepath, 'utf-8');
const startMs = performance.now();
await client.query(sql);
const durationMs = (performance.now() - startMs).toFixed(1);
console.log(` [OK] ${filename} (${durationMs}ms)`);
}
async function main(): Promise<void> {
const args = process.argv.slice(2);
const resetMode = args.includes('--reset');
console.log('ShieldX Database Migration Runner');
console.log('=================================\n');
const migrationFiles = getMigrationFiles();
console.log(`Found ${migrationFiles.length} migration(s) in ${MIGRATIONS_DIR}\n`);
const client = await createClient();
try {
if (resetMode) {
await dropAllTables(client);
}
console.log('Running migrations:\n');
for (const file of migrationFiles) {
await runMigration(client, file);
}
console.log('\nAll migrations completed successfully.');
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
console.error(`\nMigration failed: ${message}`);
process.exitCode = 1;
} finally {
await client.end();
}
}
main();