sonobat 0.1.0 → 0.2.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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/db/schema.ts","../src/db/migrate.ts","../src/mcp/server.ts","../src/mcp/tools/query.ts","../src/db/repository/host-repository.ts","../src/db/repository/service-repository.ts","../src/db/repository/vhost-repository.ts","../src/db/repository/http-endpoint-repository.ts","../src/db/repository/input-repository.ts","../src/db/repository/observation-repository.ts","../src/db/repository/credential-repository.ts","../src/db/repository/vulnerability-repository.ts","../src/mcp/tools/ingest.ts","../src/engine/ingest.ts","../src/db/repository/artifact-repository.ts","../src/parser/nmap-parser.ts","../src/types/parser.ts","../src/parser/ffuf-parser.ts","../src/parser/nuclei-parser.ts","../src/db/repository/service-observation-repository.ts","../src/db/repository/endpoint-input-repository.ts","../src/db/repository/cve-repository.ts","../src/engine/normalizer.ts","../src/mcp/tools/propose.ts","../src/engine/proposer.ts","../src/mcp/tools/mutation.ts","../src/mcp/resources.ts"],"sourcesContent":["/**\n * sonobat — AttackDataGraph for autonomous penetration testing\n *\n * MCP Server エントリポイント。\n * stdio トランスポートで LLM Agent と接続する。\n */\n\nimport Database from 'better-sqlite3';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { migrateDatabase } from './db/migrate.js';\nimport { createMcpServer } from './mcp/server.js';\n\nconst DB_PATH = process.env['SONOBAT_DB_PATH'] ?? 'sonobat.db';\nconst db = new Database(DB_PATH);\nmigrateDatabase(db);\n\nconst server = createMcpServer(db);\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","/**\n * sonobat — AttackDataGraph SQLite schema\n *\n * This schema is the single source of truth for the database structure.\n * It is derived from docs/architecture.md Section 4.\n */\n\nexport const SCHEMA_SQL = `\nPRAGMA foreign_keys = ON;\n\n-- ============================================================\n-- 実行単位(任意)\n-- ============================================================\nCREATE TABLE IF NOT EXISTS scans (\n id TEXT PRIMARY KEY,\n started_at TEXT NOT NULL,\n finished_at TEXT,\n notes TEXT\n);\n\n-- ============================================================\n-- 生出力(ファイル)参照\n-- ============================================================\nCREATE TABLE IF NOT EXISTS artifacts (\n id TEXT PRIMARY KEY,\n scan_id TEXT,\n tool TEXT NOT NULL, -- \"nmap\" | \"ffuf\" | \"nuclei\"\n kind TEXT NOT NULL, -- \"tool_output\" | \"http_request\" | \"http_response\"\n path TEXT NOT NULL, -- ローカルファイルパス or URI\n sha256 TEXT,\n captured_at TEXT NOT NULL,\n attrs_json TEXT, -- 任意メタ(コマンド、引数等)\n FOREIGN KEY (scan_id) REFERENCES scans(id) ON DELETE SET NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_artifacts_tool ON artifacts(tool);\n\n-- ============================================================\n-- ホスト\n-- ============================================================\nCREATE TABLE IF NOT EXISTS hosts (\n id TEXT PRIMARY KEY,\n authority_kind TEXT NOT NULL, -- \"IP\" | \"DOMAIN\"\n authority TEXT NOT NULL UNIQUE, -- IP アドレスまたはドメイン名\n resolved_ips_json TEXT NOT NULL DEFAULT '[]',\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n);\n\n-- ============================================================\n-- バーチャルホスト\n-- ============================================================\nCREATE TABLE IF NOT EXISTS vhosts (\n id TEXT PRIMARY KEY,\n host_id TEXT NOT NULL,\n hostname TEXT NOT NULL,\n source TEXT, -- \"nmap\" | \"cert\" | \"header\" | \"manual\"\n evidence_artifact_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n UNIQUE (host_id, hostname),\n FOREIGN KEY (host_id) REFERENCES hosts(id) ON DELETE CASCADE,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_vhosts_host ON vhosts(host_id);\n\n-- ============================================================\n-- サービス\n-- ============================================================\nCREATE TABLE IF NOT EXISTS services (\n id TEXT PRIMARY KEY,\n host_id TEXT NOT NULL,\n transport TEXT NOT NULL, -- \"tcp\" | \"udp\"\n port INTEGER NOT NULL,\n app_proto TEXT NOT NULL, -- \"http\" | \"ssh\" | \"ftp\" 等\n proto_confidence TEXT NOT NULL, -- \"high\" | \"medium\" | \"low\"\n banner TEXT,\n product TEXT,\n version TEXT,\n state TEXT NOT NULL, -- \"open\" | \"closed\" | \"filtered\"\n evidence_artifact_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE (host_id, transport, port),\n FOREIGN KEY (host_id) REFERENCES hosts(id) ON DELETE CASCADE,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_services_host ON services(host_id);\n\n-- ============================================================\n-- サービス観測(key-value)\n-- ============================================================\nCREATE TABLE IF NOT EXISTS service_observations (\n id TEXT PRIMARY KEY,\n service_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n confidence TEXT NOT NULL, -- \"high\" | \"medium\" | \"low\"\n evidence_artifact_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_svc_obs_service ON service_observations(service_id);\n\n-- ============================================================\n-- HTTP エンドポイント\n-- ============================================================\nCREATE TABLE IF NOT EXISTS http_endpoints (\n id TEXT PRIMARY KEY,\n service_id TEXT NOT NULL,\n vhost_id TEXT,\n base_uri TEXT NOT NULL, -- \"http://example.com:80\"\n method TEXT NOT NULL, -- \"GET\" | \"POST\" | ...\n path TEXT NOT NULL, -- \"/admin\"(クエリは含めない)\n status_code INTEGER,\n content_length INTEGER,\n words INTEGER,\n lines INTEGER,\n evidence_artifact_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n UNIQUE (service_id, method, path),\n FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE,\n FOREIGN KEY (vhost_id) REFERENCES vhosts(id) ON DELETE SET NULL,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_endpoints_service ON http_endpoints(service_id);\n\n-- ============================================================\n-- 入力パラメータ\n-- ============================================================\nCREATE TABLE IF NOT EXISTS inputs (\n id TEXT PRIMARY KEY,\n service_id TEXT NOT NULL,\n location TEXT NOT NULL, -- \"query\" | \"path\" | \"body\" | \"header\" | \"cookie\"\n name TEXT NOT NULL,\n type_hint TEXT, -- \"string\" | \"int\" | \"json\" 等(任意)\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE (service_id, location, name),\n FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_inputs_service ON inputs(service_id);\n\n-- ============================================================\n-- エンドポイント ↔ 入力(多対多)\n-- ============================================================\nCREATE TABLE IF NOT EXISTS endpoint_inputs (\n id TEXT PRIMARY KEY,\n endpoint_id TEXT NOT NULL,\n input_id TEXT NOT NULL,\n evidence_artifact_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n UNIQUE (endpoint_id, input_id),\n FOREIGN KEY (endpoint_id) REFERENCES http_endpoints(id) ON DELETE CASCADE,\n FOREIGN KEY (input_id) REFERENCES inputs(id) ON DELETE CASCADE,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_ep_inputs_endpoint ON endpoint_inputs(endpoint_id);\nCREATE INDEX IF NOT EXISTS idx_ep_inputs_input ON endpoint_inputs(input_id);\n\n-- ============================================================\n-- 観測値\n-- ============================================================\nCREATE TABLE IF NOT EXISTS observations (\n id TEXT PRIMARY KEY,\n input_id TEXT NOT NULL,\n raw_value TEXT NOT NULL,\n norm_value TEXT NOT NULL,\n body_path TEXT, -- JSON Pointer 等(例: \"/user/name\")\n source TEXT NOT NULL, -- \"ffuf_url\" | \"req_query\" | \"req_body\" | \"manual\"\n confidence TEXT NOT NULL, -- \"high\" | \"medium\" | \"low\"\n evidence_artifact_id TEXT NOT NULL,\n observed_at TEXT NOT NULL,\n FOREIGN KEY (input_id) REFERENCES inputs(id) ON DELETE CASCADE,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_obs_input ON observations(input_id);\n\n-- ============================================================\n-- 認証情報\n-- ============================================================\nCREATE TABLE IF NOT EXISTS credentials (\n id TEXT PRIMARY KEY,\n service_id TEXT NOT NULL,\n endpoint_id TEXT, -- HTTP の場合のみ(任意)\n username TEXT NOT NULL,\n secret TEXT NOT NULL,\n secret_type TEXT NOT NULL, -- \"password\" | \"token\" | \"api_key\" | \"ssh_key\"\n source TEXT NOT NULL, -- \"brute_force\" | \"default\" | \"leaked\" | \"manual\"\n confidence TEXT NOT NULL, -- \"high\" | \"medium\" | \"low\"\n evidence_artifact_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE,\n FOREIGN KEY (endpoint_id) REFERENCES http_endpoints(id) ON DELETE SET NULL,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_creds_service ON credentials(service_id);\nCREATE INDEX IF NOT EXISTS idx_creds_endpoint ON credentials(endpoint_id);\n\n-- ============================================================\n-- 脆弱性\n-- ============================================================\nCREATE TABLE IF NOT EXISTS vulnerabilities (\n id TEXT PRIMARY KEY,\n service_id TEXT NOT NULL,\n endpoint_id TEXT, -- HTTP の場合のみ(任意)\n vuln_type TEXT NOT NULL, -- \"sqli\" | \"xss\" | \"rce\" | \"lfi\" | \"ssrf\" | ...\n title TEXT NOT NULL,\n description TEXT,\n severity TEXT NOT NULL, -- \"critical\" | \"high\" | \"medium\" | \"low\" | \"info\"\n confidence TEXT NOT NULL, -- \"high\" | \"medium\" | \"low\"\n evidence_artifact_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE,\n FOREIGN KEY (endpoint_id) REFERENCES http_endpoints(id) ON DELETE SET NULL,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_vulns_service ON vulnerabilities(service_id);\nCREATE INDEX IF NOT EXISTS idx_vulns_endpoint ON vulnerabilities(endpoint_id);\nCREATE INDEX IF NOT EXISTS idx_vulns_severity ON vulnerabilities(severity);\n\n-- ============================================================\n-- CVE 情報\n-- ============================================================\nCREATE TABLE IF NOT EXISTS cves (\n id TEXT PRIMARY KEY,\n vulnerability_id TEXT NOT NULL,\n cve_id TEXT NOT NULL, -- \"CVE-YYYY-NNNNN\"\n description TEXT,\n cvss_score REAL,\n cvss_vector TEXT,\n reference_url TEXT,\n created_at TEXT NOT NULL,\n FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_cves_vuln ON cves(vulnerability_id);\nCREATE INDEX IF NOT EXISTS idx_cves_cveid ON cves(cve_id);\n` as const;\n","import type Database from 'better-sqlite3';\nimport { SCHEMA_SQL } from './schema.js';\n\n/**\n * Run schema SQL to create all tables.\n * Enables foreign key enforcement before executing the schema.\n */\nexport function migrateDatabase(db: Database.Database): void {\n db.pragma('foreign_keys = ON');\n db.exec(SCHEMA_SQL);\n}\n","/**\n * sonobat — MCP Server\n *\n * Creates and configures the MCP server with all tools and resources.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type Database from 'better-sqlite3';\nimport { registerQueryTools } from './tools/query.js';\nimport { registerIngestTool } from './tools/ingest.js';\nimport { registerProposeTool } from './tools/propose.js';\nimport { registerMutationTools } from './tools/mutation.js';\nimport { registerResources } from './resources.js';\n\n/**\n * Create a fully configured MCP server with all sonobat tools and resources.\n *\n * @param db - The better-sqlite3 database instance\n * @returns Configured McpServer instance\n */\nexport function createMcpServer(db: Database.Database): McpServer {\n const server = new McpServer({\n name: 'sonobat',\n version: '0.1.0',\n });\n\n // Register tools\n registerQueryTools(server, db);\n registerIngestTool(server, db);\n registerProposeTool(server, db);\n registerMutationTools(server, db);\n\n // Register resources\n registerResources(server, db);\n\n return server;\n}\n","/**\n * sonobat — MCP Query Tools\n *\n * Read-only tools for querying the AttackDataGraph.\n */\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type Database from 'better-sqlite3';\nimport { z } from 'zod';\nimport { HostRepository } from '../../db/repository/host-repository.js';\nimport { ServiceRepository } from '../../db/repository/service-repository.js';\nimport { VhostRepository } from '../../db/repository/vhost-repository.js';\nimport { HttpEndpointRepository } from '../../db/repository/http-endpoint-repository.js';\nimport { InputRepository } from '../../db/repository/input-repository.js';\nimport { ObservationRepository } from '../../db/repository/observation-repository.js';\nimport { CredentialRepository } from '../../db/repository/credential-repository.js';\nimport { VulnerabilityRepository } from '../../db/repository/vulnerability-repository.js';\n\nexport function registerQueryTools(server: McpServer, db: Database.Database): void {\n const hostRepo = new HostRepository(db);\n const serviceRepo = new ServiceRepository(db);\n const vhostRepo = new VhostRepository(db);\n const httpEndpointRepo = new HttpEndpointRepository(db);\n const inputRepo = new InputRepository(db);\n const observationRepo = new ObservationRepository(db);\n const credentialRepo = new CredentialRepository(db);\n const vulnRepo = new VulnerabilityRepository(db);\n\n // 1. list_hosts\n server.tool('list_hosts', 'List all discovered hosts', {}, async () => {\n const hosts = hostRepo.findAll();\n return { content: [{ type: 'text', text: JSON.stringify(hosts, null, 2) }] };\n });\n\n // 2. get_host\n server.tool(\n 'get_host',\n 'Get detailed information about a host including services and vhosts',\n { hostId: z.string().describe('Host UUID') },\n async ({ hostId }) => {\n const host = hostRepo.findById(hostId);\n if (!host) {\n return { content: [{ type: 'text', text: `Host not found: ${hostId}` }], isError: true };\n }\n const services = serviceRepo.findByHostId(hostId);\n const vhosts = vhostRepo.findByHostId(hostId);\n const result = { ...host, services, vhosts };\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n },\n );\n\n // 3. list_services\n server.tool(\n 'list_services',\n 'List all services for a host',\n { hostId: z.string().describe('Host UUID') },\n async ({ hostId }) => {\n const services = serviceRepo.findByHostId(hostId);\n return { content: [{ type: 'text', text: JSON.stringify(services, null, 2) }] };\n },\n );\n\n // 4. list_endpoints\n server.tool(\n 'list_endpoints',\n 'List all HTTP endpoints for a service',\n { serviceId: z.string().describe('Service UUID') },\n async ({ serviceId }) => {\n const endpoints = httpEndpointRepo.findByServiceId(serviceId);\n return { content: [{ type: 'text', text: JSON.stringify(endpoints, null, 2) }] };\n },\n );\n\n // 5. list_inputs\n server.tool(\n 'list_inputs',\n 'List all input parameters for a service, optionally filtered by location',\n {\n serviceId: z.string().describe('Service UUID'),\n location: z.string().optional().describe('Filter by location (query, path, body, header, cookie)'),\n },\n async ({ serviceId, location }) => {\n const inputs = inputRepo.findByServiceId(serviceId, location);\n return { content: [{ type: 'text', text: JSON.stringify(inputs, null, 2) }] };\n },\n );\n\n // 6. list_observations\n server.tool(\n 'list_observations',\n 'List all observations for an input parameter',\n { inputId: z.string().describe('Input UUID') },\n async ({ inputId }) => {\n const observations = observationRepo.findByInputId(inputId);\n return { content: [{ type: 'text', text: JSON.stringify(observations, null, 2) }] };\n },\n );\n\n // 7. list_credentials\n server.tool(\n 'list_credentials',\n 'List credentials, optionally filtered by service',\n {\n serviceId: z.string().optional().describe('Service UUID (optional, omit to list all)'),\n },\n async ({ serviceId }) => {\n const credentials = serviceId\n ? credentialRepo.findByServiceId(serviceId)\n : credentialRepo.findAll();\n return { content: [{ type: 'text', text: JSON.stringify(credentials, null, 2) }] };\n },\n );\n\n // 8. list_vulnerabilities\n server.tool(\n 'list_vulnerabilities',\n 'List vulnerabilities, optionally filtered by service and/or severity',\n {\n serviceId: z.string().optional().describe('Service UUID (optional, omit to list all)'),\n severity: z.string().optional().describe('Filter by severity (critical, high, medium, low, info)'),\n },\n async ({ serviceId, severity }) => {\n const vulns = serviceId\n ? vulnRepo.findByServiceId(serviceId, severity)\n : vulnRepo.findAll(severity);\n return { content: [{ type: 'text', text: JSON.stringify(vulns, null, 2) }] };\n },\n );\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Host } from '../../types/entities.js';\nimport type { CreateHostInput, UpdateHostInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `hosts` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface HostRow {\n id: string;\n authority_kind: string;\n authority: string;\n resolved_ips_json: string;\n created_at: string;\n updated_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Host entity. */\nfunction rowToHost(row: HostRow): Host {\n return {\n id: row.id,\n authorityKind: row.authority_kind,\n authority: row.authority,\n resolvedIpsJson: row.resolved_ips_json,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\n/**\n * Repository for the `hosts` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class HostRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Host and return the full entity. */\n create(input: CreateHostInput): Host {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string, string, string, string]\n >(\n `INSERT INTO hosts (id, authority_kind, authority, resolved_ips_json, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.authorityKind,\n input.authority,\n input.resolvedIpsJson,\n now,\n now,\n );\n\n return {\n id,\n authorityKind: input.authorityKind,\n authority: input.authority,\n resolvedIpsJson: input.resolvedIpsJson,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n /** Find a Host by its primary key. Returns undefined if not found. */\n findById(id: string): Host | undefined {\n const stmt = this.db.prepare<[string], HostRow>(\n `SELECT id, authority_kind, authority, resolved_ips_json, created_at, updated_at\n FROM hosts\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToHost(row) : undefined;\n }\n\n /** Return all Hosts. */\n findAll(): Host[] {\n const stmt = this.db.prepare<[], HostRow>(\n `SELECT id, authority_kind, authority, resolved_ips_json, created_at, updated_at\n FROM hosts`,\n );\n\n const rows = stmt.all();\n return rows.map(rowToHost);\n }\n\n /** Find a Host by its unique authority value. Returns undefined if not found. */\n findByAuthority(authority: string): Host | undefined {\n const stmt = this.db.prepare<[string], HostRow>(\n `SELECT id, authority_kind, authority, resolved_ips_json, created_at, updated_at\n FROM hosts\n WHERE authority = ?`,\n );\n\n const row = stmt.get(authority);\n return row ? rowToHost(row) : undefined;\n }\n\n /**\n * Update an existing Host with the provided fields.\n * Always bumps `updated_at`. Returns the updated entity, or undefined if\n * the Host was not found.\n */\n update(id: string, input: UpdateHostInput): Host | undefined {\n const setClauses: string[] = [];\n const params: unknown[] = [];\n\n if (input.resolvedIpsJson !== undefined) {\n setClauses.push('resolved_ips_json = ?');\n params.push(input.resolvedIpsJson);\n }\n\n const now = new Date().toISOString();\n setClauses.push('updated_at = ?');\n params.push(now);\n\n // Always include the WHERE id\n params.push(id);\n\n const sql = `UPDATE hosts SET ${setClauses.join(', ')} WHERE id = ?`;\n const stmt = this.db.prepare(sql);\n const result = stmt.run(...params);\n\n if (result.changes === 0) {\n return undefined;\n }\n\n return this.findById(id);\n }\n\n /** Delete a Host by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM hosts WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Service } from '../../types/entities.js';\nimport type { CreateServiceInput, UpdateServiceInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `services` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface ServiceRow {\n id: string;\n host_id: string;\n transport: string;\n port: number;\n app_proto: string;\n proto_confidence: string;\n banner: string | null;\n product: string | null;\n version: string | null;\n state: string;\n evidence_artifact_id: string;\n created_at: string;\n updated_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Service entity. */\nfunction rowToService(row: ServiceRow): Service {\n return {\n id: row.id,\n hostId: row.host_id,\n transport: row.transport,\n port: row.port,\n appProto: row.app_proto,\n protoConfidence: row.proto_confidence,\n banner: row.banner ?? undefined,\n product: row.product ?? undefined,\n version: row.version ?? undefined,\n state: row.state,\n evidenceArtifactId: row.evidence_artifact_id,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\n/**\n * Repository for the `services` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class ServiceRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Service and return the full entity. */\n create(input: CreateServiceInput): Service {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string, number, string, string, string | null, string | null, string | null, string, string, string, string]\n >(\n `INSERT INTO services (id, host_id, transport, port, app_proto, proto_confidence, banner, product, version, state, evidence_artifact_id, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.hostId,\n input.transport,\n input.port,\n input.appProto,\n input.protoConfidence,\n input.banner ?? null,\n input.product ?? null,\n input.version ?? null,\n input.state,\n input.evidenceArtifactId,\n now,\n now,\n );\n\n return {\n id,\n hostId: input.hostId,\n transport: input.transport,\n port: input.port,\n appProto: input.appProto,\n protoConfidence: input.protoConfidence,\n banner: input.banner,\n product: input.product,\n version: input.version,\n state: input.state,\n evidenceArtifactId: input.evidenceArtifactId,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n /** Find a Service by its primary key. Returns undefined if not found. */\n findById(id: string): Service | undefined {\n const stmt = this.db.prepare<[string], ServiceRow>(\n `SELECT id, host_id, transport, port, app_proto, proto_confidence, banner, product, version, state, evidence_artifact_id, created_at, updated_at\n FROM services\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToService(row) : undefined;\n }\n\n /** Find all Services belonging to a given host. */\n findByHostId(hostId: string): Service[] {\n const stmt = this.db.prepare<[string], ServiceRow>(\n `SELECT id, host_id, transport, port, app_proto, proto_confidence, banner, product, version, state, evidence_artifact_id, created_at, updated_at\n FROM services\n WHERE host_id = ?`,\n );\n\n const rows = stmt.all(hostId);\n return rows.map(rowToService);\n }\n\n /**\n * Update an existing Service with the provided fields.\n * Always bumps `updated_at`. Returns the updated entity, or undefined if\n * the Service was not found.\n */\n update(id: string, input: UpdateServiceInput): Service | undefined {\n const setClauses: string[] = [];\n const params: unknown[] = [];\n\n if (input.appProto !== undefined) {\n setClauses.push('app_proto = ?');\n params.push(input.appProto);\n }\n\n if (input.protoConfidence !== undefined) {\n setClauses.push('proto_confidence = ?');\n params.push(input.protoConfidence);\n }\n\n if (input.banner !== undefined) {\n setClauses.push('banner = ?');\n params.push(input.banner);\n }\n\n if (input.product !== undefined) {\n setClauses.push('product = ?');\n params.push(input.product);\n }\n\n if (input.version !== undefined) {\n setClauses.push('version = ?');\n params.push(input.version);\n }\n\n if (input.state !== undefined) {\n setClauses.push('state = ?');\n params.push(input.state);\n }\n\n const now = new Date().toISOString();\n setClauses.push('updated_at = ?');\n params.push(now);\n\n // Always include the WHERE id\n params.push(id);\n\n const sql = `UPDATE services SET ${setClauses.join(', ')} WHERE id = ?`;\n const stmt = this.db.prepare(sql);\n const result = stmt.run(...params);\n\n if (result.changes === 0) {\n return undefined;\n }\n\n return this.findById(id);\n }\n\n /** Delete a Service by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM services WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Vhost } from '../../types/entities.js';\nimport type { CreateVhostInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `vhosts` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface VhostRow {\n id: string;\n host_id: string;\n hostname: string;\n source: string | null;\n evidence_artifact_id: string;\n created_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Vhost entity. */\nfunction rowToVhost(row: VhostRow): Vhost {\n return {\n id: row.id,\n hostId: row.host_id,\n hostname: row.hostname,\n source: row.source ?? undefined,\n evidenceArtifactId: row.evidence_artifact_id,\n createdAt: row.created_at,\n };\n}\n\n/**\n * Repository for the `vhosts` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class VhostRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Vhost and return the full entity. */\n create(input: CreateVhostInput): Vhost {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string, string | null, string, string]\n >(\n `INSERT INTO vhosts (id, host_id, hostname, source, evidence_artifact_id, created_at)\n VALUES (?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.hostId,\n input.hostname,\n input.source ?? null,\n input.evidenceArtifactId,\n now,\n );\n\n return {\n id,\n hostId: input.hostId,\n hostname: input.hostname,\n source: input.source,\n evidenceArtifactId: input.evidenceArtifactId,\n createdAt: now,\n };\n }\n\n /** Find a Vhost by its primary key. Returns undefined if not found. */\n findById(id: string): Vhost | undefined {\n const stmt = this.db.prepare<[string], VhostRow>(\n `SELECT id, host_id, hostname, source, evidence_artifact_id, created_at\n FROM vhosts\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToVhost(row) : undefined;\n }\n\n /** Return all Vhosts belonging to a given host. */\n findByHostId(hostId: string): Vhost[] {\n const stmt = this.db.prepare<[string], VhostRow>(\n `SELECT id, host_id, hostname, source, evidence_artifact_id, created_at\n FROM vhosts\n WHERE host_id = ?`,\n );\n\n const rows = stmt.all(hostId);\n return rows.map(rowToVhost);\n }\n\n /** Delete a Vhost by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM vhosts WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { HttpEndpoint } from '../../types/entities.js';\nimport type { CreateHttpEndpointInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `http_endpoints` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface HttpEndpointRow {\n id: string;\n service_id: string;\n vhost_id: string | null;\n base_uri: string;\n method: string;\n path: string;\n status_code: number | null;\n content_length: number | null;\n words: number | null;\n lines: number | null;\n evidence_artifact_id: string;\n created_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase HttpEndpoint entity. */\nfunction rowToHttpEndpoint(row: HttpEndpointRow): HttpEndpoint {\n return {\n id: row.id,\n serviceId: row.service_id,\n vhostId: row.vhost_id ?? undefined,\n baseUri: row.base_uri,\n method: row.method,\n path: row.path,\n statusCode: row.status_code ?? undefined,\n contentLength: row.content_length ?? undefined,\n words: row.words ?? undefined,\n lines: row.lines ?? undefined,\n evidenceArtifactId: row.evidence_artifact_id,\n createdAt: row.created_at,\n };\n}\n\n/**\n * Repository for the `http_endpoints` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class HttpEndpointRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new HttpEndpoint and return the full entity. */\n create(input: CreateHttpEndpointInput): HttpEndpoint {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare(\n `INSERT INTO http_endpoints (id, service_id, vhost_id, base_uri, method, path, status_code, content_length, words, lines, evidence_artifact_id, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.serviceId,\n input.vhostId ?? null,\n input.baseUri,\n input.method,\n input.path,\n input.statusCode ?? null,\n input.contentLength ?? null,\n input.words ?? null,\n input.lines ?? null,\n input.evidenceArtifactId,\n now,\n );\n\n return {\n id,\n serviceId: input.serviceId,\n vhostId: input.vhostId,\n baseUri: input.baseUri,\n method: input.method,\n path: input.path,\n statusCode: input.statusCode,\n contentLength: input.contentLength,\n words: input.words,\n lines: input.lines,\n evidenceArtifactId: input.evidenceArtifactId,\n createdAt: now,\n };\n }\n\n /** Find an HttpEndpoint by its primary key. Returns undefined if not found. */\n findById(id: string): HttpEndpoint | undefined {\n const stmt = this.db.prepare<[string], HttpEndpointRow>(\n `SELECT id, service_id, vhost_id, base_uri, method, path, status_code, content_length, words, lines, evidence_artifact_id, created_at\n FROM http_endpoints\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToHttpEndpoint(row) : undefined;\n }\n\n /** Return all HttpEndpoints for a given service. */\n findByServiceId(serviceId: string): HttpEndpoint[] {\n const stmt = this.db.prepare<[string], HttpEndpointRow>(\n `SELECT id, service_id, vhost_id, base_uri, method, path, status_code, content_length, words, lines, evidence_artifact_id, created_at\n FROM http_endpoints\n WHERE service_id = ?`,\n );\n\n const rows = stmt.all(serviceId);\n return rows.map(rowToHttpEndpoint);\n }\n\n /** Delete an HttpEndpoint by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>(\n 'DELETE FROM http_endpoints WHERE id = ?',\n );\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Input } from '../../types/entities.js';\nimport type { CreateInputInput, UpdateInputInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `inputs` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface InputRow {\n id: string;\n service_id: string;\n location: string;\n name: string;\n type_hint: string | null;\n created_at: string;\n updated_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Input entity. */\nfunction rowToInput(row: InputRow): Input {\n return {\n id: row.id,\n serviceId: row.service_id,\n location: row.location,\n name: row.name,\n typeHint: row.type_hint ?? undefined,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\n/**\n * Repository for the `inputs` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class InputRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Input and return the full entity. */\n create(input: CreateInputInput): Input {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string, string, string | null, string, string]\n >(\n `INSERT INTO inputs (id, service_id, location, name, type_hint, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.serviceId,\n input.location,\n input.name,\n input.typeHint ?? null,\n now,\n now,\n );\n\n return {\n id,\n serviceId: input.serviceId,\n location: input.location,\n name: input.name,\n typeHint: input.typeHint,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n /** Find an Input by its primary key. Returns undefined if not found. */\n findById(id: string): Input | undefined {\n const stmt = this.db.prepare<[string], InputRow>(\n `SELECT id, service_id, location, name, type_hint, created_at, updated_at\n FROM inputs\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToInput(row) : undefined;\n }\n\n /**\n * Find all Inputs for a given service.\n * If location is provided, further filter by location.\n */\n findByServiceId(serviceId: string, location?: string): Input[] {\n if (location !== undefined) {\n const stmt = this.db.prepare<[string, string], InputRow>(\n `SELECT id, service_id, location, name, type_hint, created_at, updated_at\n FROM inputs\n WHERE service_id = ? AND location = ?`,\n );\n\n const rows = stmt.all(serviceId, location);\n return rows.map(rowToInput);\n }\n\n const stmt = this.db.prepare<[string], InputRow>(\n `SELECT id, service_id, location, name, type_hint, created_at, updated_at\n FROM inputs\n WHERE service_id = ?`,\n );\n\n const rows = stmt.all(serviceId);\n return rows.map(rowToInput);\n }\n\n /**\n * Update an existing Input with the provided fields.\n * Always bumps `updated_at`. Returns the updated entity, or undefined if\n * the Input was not found.\n */\n update(id: string, input: UpdateInputInput): Input | undefined {\n const setClauses: string[] = [];\n const params: unknown[] = [];\n\n if (input.typeHint !== undefined) {\n setClauses.push('type_hint = ?');\n params.push(input.typeHint);\n }\n\n const now = new Date().toISOString();\n setClauses.push('updated_at = ?');\n params.push(now);\n\n // Always include the WHERE id\n params.push(id);\n\n const sql = `UPDATE inputs SET ${setClauses.join(', ')} WHERE id = ?`;\n const stmt = this.db.prepare(sql);\n const result = stmt.run(...params);\n\n if (result.changes === 0) {\n return undefined;\n }\n\n return this.findById(id);\n }\n\n /** Delete an Input by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM inputs WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Observation } from '../../types/entities.js';\nimport type { CreateObservationInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `observations` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface ObservationRow {\n id: string;\n input_id: string;\n raw_value: string;\n norm_value: string;\n body_path: string | null;\n source: string;\n confidence: string;\n evidence_artifact_id: string;\n observed_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Observation entity. */\nfunction rowToObservation(row: ObservationRow): Observation {\n return {\n id: row.id,\n inputId: row.input_id,\n rawValue: row.raw_value,\n normValue: row.norm_value,\n bodyPath: row.body_path ?? undefined,\n source: row.source,\n confidence: row.confidence,\n evidenceArtifactId: row.evidence_artifact_id,\n observedAt: row.observed_at,\n };\n}\n\n/**\n * Repository for the `observations` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class ObservationRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Observation and return the full entity. */\n create(input: CreateObservationInput): Observation {\n const id = crypto.randomUUID();\n\n const stmt = this.db.prepare<\n [string, string, string, string, string | null, string, string, string, string]\n >(\n `INSERT INTO observations (id, input_id, raw_value, norm_value, body_path, source, confidence, evidence_artifact_id, observed_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.inputId,\n input.rawValue,\n input.normValue,\n input.bodyPath ?? null,\n input.source,\n input.confidence,\n input.evidenceArtifactId,\n input.observedAt,\n );\n\n return {\n id,\n inputId: input.inputId,\n rawValue: input.rawValue,\n normValue: input.normValue,\n bodyPath: input.bodyPath,\n source: input.source,\n confidence: input.confidence,\n evidenceArtifactId: input.evidenceArtifactId,\n observedAt: input.observedAt,\n };\n }\n\n /** Find an Observation by its primary key. Returns undefined if not found. */\n findById(id: string): Observation | undefined {\n const stmt = this.db.prepare<[string], ObservationRow>(\n `SELECT id, input_id, raw_value, norm_value, body_path, source, confidence, evidence_artifact_id, observed_at\n FROM observations\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToObservation(row) : undefined;\n }\n\n /** Return all Observations for a given input ID. */\n findByInputId(inputId: string): Observation[] {\n const stmt = this.db.prepare<[string], ObservationRow>(\n `SELECT id, input_id, raw_value, norm_value, body_path, source, confidence, evidence_artifact_id, observed_at\n FROM observations\n WHERE input_id = ?`,\n );\n\n const rows = stmt.all(inputId);\n return rows.map(rowToObservation);\n }\n\n /** Delete an Observation by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM observations WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Credential } from '../../types/entities.js';\nimport type { CreateCredentialInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `credentials` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface CredentialRow {\n id: string;\n service_id: string;\n endpoint_id: string | null;\n username: string;\n secret: string;\n secret_type: string;\n source: string;\n confidence: string;\n evidence_artifact_id: string;\n created_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Credential entity. */\nfunction rowToCredential(row: CredentialRow): Credential {\n return {\n id: row.id,\n serviceId: row.service_id,\n endpointId: row.endpoint_id ?? undefined,\n username: row.username,\n secret: row.secret,\n secretType: row.secret_type,\n source: row.source,\n confidence: row.confidence,\n evidenceArtifactId: row.evidence_artifact_id,\n createdAt: row.created_at,\n };\n}\n\n/**\n * Repository for the `credentials` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class CredentialRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Credential and return the full entity. */\n create(input: CreateCredentialInput): Credential {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string | null, string, string, string, string, string, string, string]\n >(\n `INSERT INTO credentials (id, service_id, endpoint_id, username, secret, secret_type, source, confidence, evidence_artifact_id, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.serviceId,\n input.endpointId ?? null,\n input.username,\n input.secret,\n input.secretType,\n input.source,\n input.confidence,\n input.evidenceArtifactId,\n now,\n );\n\n return {\n id,\n serviceId: input.serviceId,\n endpointId: input.endpointId ?? undefined,\n username: input.username,\n secret: input.secret,\n secretType: input.secretType,\n source: input.source,\n confidence: input.confidence,\n evidenceArtifactId: input.evidenceArtifactId,\n createdAt: now,\n };\n }\n\n /** Find a Credential by its primary key. Returns undefined if not found. */\n findById(id: string): Credential | undefined {\n const stmt = this.db.prepare<[string], CredentialRow>(\n `SELECT id, service_id, endpoint_id, username, secret, secret_type, source, confidence, evidence_artifact_id, created_at\n FROM credentials\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToCredential(row) : undefined;\n }\n\n /** Return all Credentials for a given service. */\n findByServiceId(serviceId: string): Credential[] {\n const stmt = this.db.prepare<[string], CredentialRow>(\n `SELECT id, service_id, endpoint_id, username, secret, secret_type, source, confidence, evidence_artifact_id, created_at\n FROM credentials\n WHERE service_id = ?`,\n );\n\n const rows = stmt.all(serviceId);\n return rows.map(rowToCredential);\n }\n\n /** Return all Credentials across all services. */\n findAll(): Credential[] {\n const stmt = this.db.prepare<[], CredentialRow>(\n `SELECT id, service_id, endpoint_id, username, secret, secret_type, source, confidence, evidence_artifact_id, created_at\n FROM credentials`,\n );\n\n const rows = stmt.all();\n return rows.map(rowToCredential);\n }\n\n /** Delete a Credential by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM credentials WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Vulnerability } from '../../types/entities.js';\nimport type { CreateVulnerabilityInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `vulnerabilities` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface VulnerabilityRow {\n id: string;\n service_id: string;\n endpoint_id: string | null;\n vuln_type: string;\n title: string;\n description: string | null;\n severity: string;\n confidence: string;\n evidence_artifact_id: string;\n created_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Vulnerability entity. */\nfunction rowToVulnerability(row: VulnerabilityRow): Vulnerability {\n return {\n id: row.id,\n serviceId: row.service_id,\n endpointId: row.endpoint_id ?? undefined,\n vulnType: row.vuln_type,\n title: row.title,\n description: row.description ?? undefined,\n severity: row.severity,\n confidence: row.confidence,\n evidenceArtifactId: row.evidence_artifact_id,\n createdAt: row.created_at,\n };\n}\n\n/**\n * Repository for the `vulnerabilities` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class VulnerabilityRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Vulnerability and return the full entity. */\n create(input: CreateVulnerabilityInput): Vulnerability {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare(\n `INSERT INTO vulnerabilities (id, service_id, endpoint_id, vuln_type, title, description, severity, confidence, evidence_artifact_id, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.serviceId,\n input.endpointId ?? null,\n input.vulnType,\n input.title,\n input.description ?? null,\n input.severity,\n input.confidence,\n input.evidenceArtifactId,\n now,\n );\n\n return {\n id,\n serviceId: input.serviceId,\n endpointId: input.endpointId,\n vulnType: input.vulnType,\n title: input.title,\n description: input.description,\n severity: input.severity,\n confidence: input.confidence,\n evidenceArtifactId: input.evidenceArtifactId,\n createdAt: now,\n };\n }\n\n /** Find a Vulnerability by its primary key. Returns undefined if not found. */\n findById(id: string): Vulnerability | undefined {\n const stmt = this.db.prepare<[string], VulnerabilityRow>(\n `SELECT id, service_id, endpoint_id, vuln_type, title, description, severity, confidence, evidence_artifact_id, created_at\n FROM vulnerabilities\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToVulnerability(row) : undefined;\n }\n\n /**\n * Return all Vulnerabilities for a given service.\n * Optionally filter by severity when the parameter is provided.\n */\n findByServiceId(serviceId: string, severity?: string): Vulnerability[] {\n if (severity !== undefined) {\n const stmt = this.db.prepare<[string, string], VulnerabilityRow>(\n `SELECT id, service_id, endpoint_id, vuln_type, title, description, severity, confidence, evidence_artifact_id, created_at\n FROM vulnerabilities\n WHERE service_id = ? AND severity = ?`,\n );\n\n const rows = stmt.all(serviceId, severity);\n return rows.map(rowToVulnerability);\n }\n\n const stmt = this.db.prepare<[string], VulnerabilityRow>(\n `SELECT id, service_id, endpoint_id, vuln_type, title, description, severity, confidence, evidence_artifact_id, created_at\n FROM vulnerabilities\n WHERE service_id = ?`,\n );\n\n const rows = stmt.all(serviceId);\n return rows.map(rowToVulnerability);\n }\n\n /**\n * Return all Vulnerabilities across all services.\n * Optionally filter by severity when the parameter is provided.\n */\n findAll(severity?: string): Vulnerability[] {\n if (severity !== undefined) {\n const stmt = this.db.prepare<[string], VulnerabilityRow>(\n `SELECT id, service_id, endpoint_id, vuln_type, title, description, severity, confidence, evidence_artifact_id, created_at\n FROM vulnerabilities\n WHERE severity = ?`,\n );\n\n const rows = stmt.all(severity);\n return rows.map(rowToVulnerability);\n }\n\n const stmt = this.db.prepare<[], VulnerabilityRow>(\n `SELECT id, service_id, endpoint_id, vuln_type, title, description, severity, confidence, evidence_artifact_id, created_at\n FROM vulnerabilities`,\n );\n\n const rows = stmt.all();\n return rows.map(rowToVulnerability);\n }\n\n /** Delete a Vulnerability by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM vulnerabilities WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","/**\n * sonobat — MCP Ingest Tool\n *\n * Tool for ingesting tool output files into the AttackDataGraph.\n */\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type Database from 'better-sqlite3';\nimport { z } from 'zod';\nimport { ingest } from '../../engine/ingest.js';\n\nexport function registerIngestTool(server: McpServer, db: Database.Database): void {\n server.tool(\n 'ingest_file',\n 'Ingest a tool output file (nmap XML, ffuf JSON, nuclei JSONL) into the AttackDataGraph',\n {\n path: z.string().describe('Absolute path to the tool output file'),\n tool: z.enum(['nmap', 'ffuf', 'nuclei']).describe('Tool that produced the output'),\n },\n async ({ path, tool }) => {\n try {\n const result = ingest(db, { path, tool });\n const nr = result.normalizeResult;\n const summary = [\n `Ingested ${tool} output from ${path}`,\n `Artifact ID: ${result.artifactId}`,\n `Created: ${nr.hostsCreated} hosts, ${nr.servicesCreated} services, ${nr.httpEndpointsCreated} endpoints, ${nr.inputsCreated} inputs, ${nr.observationsCreated} observations, ${nr.vulnerabilitiesCreated} vulnerabilities, ${nr.cvesCreated} CVEs`,\n ].join('\\n');\n return { content: [{ type: 'text', text: summary }] };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { content: [{ type: 'text', text: `Ingest failed: ${message}` }], isError: true };\n }\n },\n );\n}\n","/**\n * sonobat — Ingest Engine\n *\n * ツール出力ファイルを読み込み、パース・正規化してDBに格納する。\n * ingestContent() はコアロジック(ファイルシステム非依存・テスト可能)。\n * ingest() はファイル読み込みの薄いラッパー。\n */\n\nimport type Database from 'better-sqlite3';\nimport fs from 'node:fs';\nimport crypto from 'node:crypto';\nimport path from 'node:path';\nimport type { IngestInput, IngestResult } from '../types/engine.js';\nimport { ArtifactRepository } from '../db/repository/artifact-repository.js';\nimport { parseNmapXml } from '../parser/nmap-parser.js';\nimport { parseFfufJson } from '../parser/ffuf-parser.js';\nimport { parseNucleiJsonl } from '../parser/nuclei-parser.js';\nimport { normalize } from './normalizer.js';\n\n/**\n * ツール出力の文字列を直接受け取り、パース・正規化してDBに格納する。\n * ファイルシステムに依存しないため、テストから直接呼び出せる。\n *\n * @param db better-sqlite3 の Database インスタンス\n * @param tool ツール種別('nmap' | 'ffuf' | 'nuclei')\n * @param content ツール出力の文字列\n * @param filePath Artifact に記録するファイルパス\n * @returns IngestResult(artifactId + normalizeResult)\n */\nexport function ingestContent(\n db: Database.Database,\n tool: 'nmap' | 'ffuf' | 'nuclei',\n content: string,\n filePath: string,\n): IngestResult {\n // 1. SHA-256 ハッシュを計算\n const sha256 = crypto.createHash('sha256').update(content).digest('hex');\n\n // 2. Artifact を作成\n const artifactRepo = new ArtifactRepository(db);\n const artifact = artifactRepo.create({\n tool,\n kind: 'tool_output',\n path: filePath,\n sha256,\n capturedAt: new Date().toISOString(),\n });\n\n // 3. ツール種別に応じてパース\n let parseResult;\n switch (tool) {\n case 'nmap':\n parseResult = parseNmapXml(content);\n break;\n case 'ffuf':\n parseResult = parseFfufJson(content);\n break;\n case 'nuclei':\n parseResult = parseNucleiJsonl(content);\n break;\n default: {\n // never 型による網羅性チェック — 未知の tool が渡された場合はコンパイルエラー\n const _exhaustive: never = tool;\n throw new Error(`Unknown tool: ${String(_exhaustive)}`);\n }\n }\n\n // 4. 正規化してDBに書き込む\n const normalizeResult = normalize(db, artifact.id, parseResult);\n\n // 5. 結果を返す\n return {\n artifactId: artifact.id,\n normalizeResult,\n };\n}\n\n/**\n * ファイルパスからツール出力を読み込み、インジェストする。\n * ingestContent() の薄いラッパー。\n *\n * @param db better-sqlite3 の Database インスタンス\n * @param input IngestInput(path + tool)\n * @returns IngestResult\n */\nexport function ingest(db: Database.Database, input: IngestInput): IngestResult {\n const resolved = path.resolve(input.path);\n const content = fs.readFileSync(resolved, 'utf-8');\n return ingestContent(db, input.tool, content, resolved);\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Artifact } from '../../types/entities.js';\nimport type { CreateArtifactInput } from '../../types/repository.js';\n\n/** Row shape returned by better-sqlite3 for the artifacts table. */\ninterface ArtifactRow {\n id: string;\n scan_id: string | null;\n tool: string;\n kind: string;\n path: string;\n sha256: string | null;\n captured_at: string;\n attrs_json: string | null;\n}\n\n/** Maps a snake_case DB row to a camelCase Artifact entity. */\nfunction rowToArtifact(row: ArtifactRow): Artifact {\n return {\n id: row.id,\n ...(row.scan_id !== null ? { scanId: row.scan_id } : {}),\n tool: row.tool,\n kind: row.kind,\n path: row.path,\n ...(row.sha256 !== null ? { sha256: row.sha256 } : {}),\n capturedAt: row.captured_at,\n ...(row.attrs_json !== null ? { attrsJson: row.attrs_json } : {}),\n };\n}\n\n/**\n * Repository for the `artifacts` table.\n *\n * Provides CRUD operations with camelCase ↔ snake_case mapping\n * between the TypeScript entity layer and the SQLite storage layer.\n */\nexport class ArtifactRepository {\n private readonly db: Database.Database;\n\n private readonly insertStmt: Database.Statement;\n private readonly selectByIdStmt: Database.Statement;\n private readonly selectAllStmt: Database.Statement;\n private readonly selectByToolStmt: Database.Statement;\n\n constructor(db: Database.Database) {\n this.db = db;\n\n this.insertStmt = this.db.prepare(\n 'INSERT INTO artifacts (id, scan_id, tool, kind, path, sha256, captured_at, attrs_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',\n );\n\n this.selectByIdStmt = this.db.prepare(\n 'SELECT id, scan_id, tool, kind, path, sha256, captured_at, attrs_json FROM artifacts WHERE id = ?',\n );\n\n this.selectAllStmt = this.db.prepare(\n 'SELECT id, scan_id, tool, kind, path, sha256, captured_at, attrs_json FROM artifacts',\n );\n\n this.selectByToolStmt = this.db.prepare(\n 'SELECT id, scan_id, tool, kind, path, sha256, captured_at, attrs_json FROM artifacts WHERE tool = ?',\n );\n }\n\n /** Create a new Artifact record and return the full entity. */\n create(input: CreateArtifactInput): Artifact {\n const id = crypto.randomUUID();\n\n this.insertStmt.run(\n id,\n input.scanId ?? null,\n input.tool,\n input.kind,\n input.path,\n input.sha256 ?? null,\n input.capturedAt,\n input.attrsJson ?? null,\n );\n\n return {\n id,\n ...(input.scanId !== undefined ? { scanId: input.scanId } : {}),\n tool: input.tool,\n kind: input.kind,\n path: input.path,\n ...(input.sha256 !== undefined ? { sha256: input.sha256 } : {}),\n capturedAt: input.capturedAt,\n ...(input.attrsJson !== undefined ? { attrsJson: input.attrsJson } : {}),\n };\n }\n\n /** Find an Artifact by its UUID. Returns undefined if not found. */\n findById(id: string): Artifact | undefined {\n const row = this.selectByIdStmt.get(id) as ArtifactRow | undefined;\n if (row === undefined) {\n return undefined;\n }\n return rowToArtifact(row);\n }\n\n /** Return all Artifact records. */\n findAll(): Artifact[] {\n const rows = this.selectAllStmt.all() as ArtifactRow[];\n return rows.map(rowToArtifact);\n }\n\n /** Return all Artifact records for the given tool name. */\n findByTool(tool: string): Artifact[] {\n const rows = this.selectByToolStmt.all(tool) as ArtifactRow[];\n return rows.map(rowToArtifact);\n }\n}\n","/**\n * sonobat — Nmap XML パーサー\n *\n * nmap の XML 出力を解析し、ParseResult 中間表現を返す。\n * fast-xml-parser を使用して XML をパースする。\n */\n\nimport { XMLParser } from 'fast-xml-parser';\nimport type {\n ParseResult,\n ParsedHost,\n ParsedService,\n ParsedServiceObservation,\n} from '../types/parser.js';\nimport { emptyParseResult } from '../types/parser.js';\n\n// ============================================================\n// XML パース後の型定義(unknown から安全に取り出すための構造)\n// ============================================================\n\ninterface NmapAddress {\n '@_addr': string;\n '@_addrtype': string;\n}\n\ninterface NmapHostname {\n '@_name': string;\n '@_type': string;\n}\n\ninterface NmapPortState {\n '@_state': string;\n '@_reason'?: string;\n}\n\ninterface NmapServiceAttr {\n '@_name'?: string;\n '@_product'?: string;\n '@_version'?: string;\n '@_extrainfo'?: string;\n '@_tunnel'?: string;\n '@_conf'?: string;\n}\n\ninterface NmapPort {\n '@_protocol': string;\n '@_portid': string;\n state: NmapPortState;\n service?: NmapServiceAttr;\n}\n\ninterface NmapOsMatch {\n '@_name': string;\n '@_accuracy': string;\n}\n\ninterface NmapHost {\n address: NmapAddress | NmapAddress[];\n hostnames?: {\n hostname?: NmapHostname | NmapHostname[];\n };\n ports?: {\n port?: NmapPort | NmapPort[];\n };\n os?: {\n osmatch?: NmapOsMatch | NmapOsMatch[];\n };\n}\n\ninterface NmapRun {\n nmaprun: {\n host?: NmapHost | NmapHost[];\n };\n}\n\n// ============================================================\n// ユーティリティ\n// ============================================================\n\n/** 値を配列に正規化する。undefined/null は空配列を返す。 */\nfunction ensureArray<T>(value: T | T[] | undefined | null): T[] {\n if (value === undefined || value === null) {\n return [];\n }\n return Array.isArray(value) ? value : [value];\n}\n\n/** nmap の conf 属性から protoConfidence を決定する */\nfunction toProtoConfidence(conf: string | undefined): string {\n const n = conf !== undefined ? Number(conf) : 0;\n if (n === 10) return 'high';\n if (n >= 7) return 'medium';\n return 'low';\n}\n\n/** OS accuracy から confidence を決定する */\nfunction toOsConfidence(accuracy: string): string {\n const n = Number(accuracy);\n if (n >= 90) return 'high';\n if (n >= 50) return 'medium';\n return 'low';\n}\n\n/** サービス名が HTTPS を示すかどうかを判定する */\nfunction isHttps(service: NmapServiceAttr): boolean {\n return service['@_name'] === 'https' || service['@_tunnel'] === 'ssl';\n}\n\n/** product, version, extrainfo からバナー文字列を合成する */\nfunction buildBanner(service: NmapServiceAttr): string | undefined {\n const parts: string[] = [];\n if (service['@_product']) parts.push(service['@_product']);\n if (service['@_version']) parts.push(service['@_version']);\n if (service['@_extrainfo']) parts.push(service['@_extrainfo']);\n return parts.length > 0 ? parts.join(' ') : undefined;\n}\n\n/** IPv4 アドレスを address 配列から取得する */\nfunction getIpv4Address(addresses: NmapAddress[]): string | undefined {\n const ipv4 = addresses.find((a) => a['@_addrtype'] === 'ipv4');\n return ipv4?.['@_addr'];\n}\n\n// ============================================================\n// メインパーサー\n// ============================================================\n\n/**\n * nmap XML 出力をパースし、ParseResult を返す。\n *\n * @param xml - nmap の XML 出力文字列\n * @returns ParseResult 中間表現\n */\nexport function parseNmapXml(xml: string): ParseResult {\n const parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n allowBooleanAttributes: true,\n });\n\n const parsed: unknown = parser.parse(xml);\n const nmapRun = parsed as NmapRun;\n\n const result = emptyParseResult();\n\n const hosts = ensureArray(nmapRun.nmaprun?.host);\n\n for (const host of hosts) {\n processHost(host, result);\n }\n\n return result;\n}\n\n/**\n * 単一の host 要素を処理し、結果に追加する。\n */\nfunction processHost(host: NmapHost, result: ParseResult): void {\n // --- ホスト情報 ---\n const addresses = ensureArray(host.address);\n const authority = getIpv4Address(addresses);\n if (authority === undefined) {\n return; // IPv4 アドレスがないホストはスキップ\n }\n\n const parsedHost: ParsedHost = {\n authority,\n authorityKind: 'IP',\n };\n result.hosts.push(parsedHost);\n\n // --- サービス情報 ---\n const ports = ensureArray(host.ports?.port);\n const services: ParsedService[] = [];\n\n for (const port of ports) {\n const service = processPort(port, authority);\n services.push(service);\n result.services.push(service);\n }\n\n // --- OS 情報 ---\n const osMatches = ensureArray(host.os?.osmatch);\n if (osMatches.length > 0) {\n processOsMatches(osMatches, authority, services, result);\n }\n}\n\n/**\n * 単一の port 要素を処理し、ParsedService を返す。\n */\nfunction processPort(port: NmapPort, hostAuthority: string): ParsedService {\n const service = port.service;\n const serviceName = service?.['@_name'] ?? '';\n\n // appProto の決定: https or tunnel=ssl -> 'https'\n const appProto =\n service !== undefined && isHttps(service) ? 'https' : serviceName;\n\n const banner = service !== undefined ? buildBanner(service) : undefined;\n const protoConfidence = toProtoConfidence(service?.['@_conf']);\n\n return {\n hostAuthority,\n transport: port['@_protocol'],\n port: Number(port['@_portid']),\n appProto,\n protoConfidence,\n banner,\n product: service?.['@_product'],\n version: service?.['@_version'],\n state: port.state['@_state'],\n };\n}\n\n/**\n * OS マッチ情報を serviceObservations に追加する。\n * 最初のサービスの transport/port を使う。サービスがない場合は port=0, transport='tcp' を使う。\n */\nfunction processOsMatches(\n osMatches: NmapOsMatch[],\n hostAuthority: string,\n services: ParsedService[],\n result: ParseResult,\n): void {\n const firstService = services.length > 0 ? services[0] : undefined;\n const transport = firstService?.transport ?? 'tcp';\n const port = firstService?.port ?? 0;\n\n for (const osMatch of osMatches) {\n const observation: ParsedServiceObservation = {\n hostAuthority,\n transport,\n port,\n key: 'os',\n value: osMatch['@_name'],\n confidence: toOsConfidence(osMatch['@_accuracy']),\n };\n result.serviceObservations.push(observation);\n }\n}\n","/**\n * sonobat — Parser intermediate types\n *\n * パーサーは DB の ID を持たない中間表現を返す。\n * Normalizer が自然キー(authority, port 等)で DB を検索・upsert する。\n */\n\n// ============================================================\n// 中間表現(DB ID を持たない)\n// ============================================================\n\n/** ホストの中間表現 */\nexport interface ParsedHost {\n authority: string;\n authorityKind: 'IP' | 'DOMAIN';\n resolvedIps?: string[];\n}\n\n/** サービスの中間表現 */\nexport interface ParsedService {\n hostAuthority: string;\n transport: string;\n port: number;\n appProto: string;\n protoConfidence: string;\n banner?: string;\n product?: string;\n version?: string;\n state: string;\n}\n\n/** サービス観測の中間表現 */\nexport interface ParsedServiceObservation {\n hostAuthority: string;\n transport: string;\n port: number;\n key: string;\n value: string;\n confidence: string;\n}\n\n/** HTTP エンドポイントの中間表現 */\nexport interface ParsedHttpEndpoint {\n hostAuthority: string;\n port: number;\n baseUri: string;\n method: string;\n path: string;\n statusCode?: number;\n contentLength?: number;\n words?: number;\n lines?: number;\n}\n\n/** 入力パラメータの中間表現 */\nexport interface ParsedInput {\n hostAuthority: string;\n port: number;\n location: string;\n name: string;\n typeHint?: string;\n}\n\n/** エンドポイント ↔ 入力の紐づけ中間表現 */\nexport interface ParsedEndpointInput {\n hostAuthority: string;\n port: number;\n method: string;\n path: string;\n inputLocation: string;\n inputName: string;\n}\n\n/** 観測値の中間表現 */\nexport interface ParsedObservation {\n hostAuthority: string;\n port: number;\n inputLocation: string;\n inputName: string;\n rawValue: string;\n normValue: string;\n source: string;\n confidence: string;\n}\n\n/** 脆弱性の中間表現 */\nexport interface ParsedVulnerability {\n hostAuthority: string;\n port: number;\n method?: string;\n path?: string;\n vulnType: string;\n title: string;\n description?: string;\n severity: string;\n confidence: string;\n}\n\n/** CVE の中間表現 */\nexport interface ParsedCve {\n /** 対応する脆弱性の title(紐づけ用) */\n vulnerabilityTitle: string;\n cveId: string;\n description?: string;\n cvssScore?: number;\n cvssVector?: string;\n referenceUrl?: string;\n}\n\n// ============================================================\n// パース結果\n// ============================================================\n\n/** パーサーが返す統一的な結果型 */\nexport interface ParseResult {\n hosts: ParsedHost[];\n services: ParsedService[];\n serviceObservations: ParsedServiceObservation[];\n httpEndpoints: ParsedHttpEndpoint[];\n inputs: ParsedInput[];\n endpointInputs: ParsedEndpointInput[];\n observations: ParsedObservation[];\n vulnerabilities: ParsedVulnerability[];\n cves: ParsedCve[];\n}\n\n/** 空の ParseResult を生成するユーティリティ */\nexport function emptyParseResult(): ParseResult {\n return {\n hosts: [],\n services: [],\n serviceObservations: [],\n httpEndpoints: [],\n inputs: [],\n endpointInputs: [],\n observations: [],\n vulnerabilities: [],\n cves: [],\n };\n}\n","/**\n * sonobat — ffuf JSON output parser\n *\n * ffuf の JSON 出力をパースし、ParseResult に変換する。\n * パスディスカバリ、パラメータファジングの両方に対応。\n */\n\nimport type {\n ParseResult,\n ParsedHost,\n ParsedService,\n ParsedHttpEndpoint,\n ParsedInput,\n ParsedEndpointInput,\n ParsedObservation,\n} from '../types/parser.js';\nimport { emptyParseResult } from '../types/parser.js';\n\n// ---------------------------------------------------------------------------\n// 内部型: ffuf JSON 構造のバリデーション用\n// ---------------------------------------------------------------------------\n\ninterface FfufConfig {\n url: string;\n method: string;\n}\n\ninterface FfufResult {\n input: Record<string, string>;\n status: number;\n length: number;\n words: number;\n lines: number;\n url: string;\n host: string;\n}\n\ninterface FfufJson {\n commandline: string;\n config: FfufConfig;\n results: FfufResult[];\n}\n\n// ---------------------------------------------------------------------------\n// バリデーション\n// ---------------------------------------------------------------------------\n\nconst IP_REGEX = /^\\d{1,3}(\\.\\d{1,3}){3}$/;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction validateFfufJson(raw: unknown): FfufJson {\n if (!isRecord(raw)) {\n throw new Error('ffuf JSON: root must be an object');\n }\n\n if (typeof raw['commandline'] !== 'string') {\n throw new Error('ffuf JSON: commandline must be a string');\n }\n\n const config = raw['config'];\n if (!isRecord(config)) {\n throw new Error('ffuf JSON: config must be an object');\n }\n if (typeof config['url'] !== 'string' || typeof config['method'] !== 'string') {\n throw new Error('ffuf JSON: config.url and config.method must be strings');\n }\n\n if (!Array.isArray(raw['results'])) {\n throw new Error('ffuf JSON: results must be an array');\n }\n\n const results: FfufResult[] = [];\n for (const item of raw['results'] as unknown[]) {\n if (!isRecord(item)) {\n throw new Error('ffuf JSON: each result must be an object');\n }\n results.push({\n input: isRecord(item['input'])\n ? Object.fromEntries(\n Object.entries(item['input'] as Record<string, unknown>).map(([k, v]) => [\n k,\n String(v),\n ]),\n )\n : {},\n status: typeof item['status'] === 'number' ? item['status'] : 0,\n length: typeof item['length'] === 'number' ? item['length'] : 0,\n words: typeof item['words'] === 'number' ? item['words'] : 0,\n lines: typeof item['lines'] === 'number' ? item['lines'] : 0,\n url: typeof item['url'] === 'string' ? item['url'] : '',\n host: typeof item['host'] === 'string' ? item['host'] : '',\n });\n }\n\n return {\n commandline: raw['commandline'] as string,\n config: {\n url: config['url'] as string,\n method: config['method'] as string,\n },\n results,\n };\n}\n\n// ---------------------------------------------------------------------------\n// URL ユーティリティ\n// ---------------------------------------------------------------------------\n\ninterface ParsedUrl {\n scheme: string;\n hostname: string;\n port: number;\n pathname: string;\n searchParams: URLSearchParams;\n}\n\nfunction parseUrl(urlStr: string): ParsedUrl {\n const parsed = new URL(urlStr);\n const scheme = parsed.protocol.replace(':', '');\n\n let port: number;\n if (parsed.port !== '') {\n port = Number(parsed.port);\n } else {\n port = scheme === 'https' ? 443 : 80;\n }\n\n return {\n scheme,\n hostname: parsed.hostname,\n port,\n pathname: parsed.pathname,\n searchParams: parsed.searchParams,\n };\n}\n\nfunction determineAuthorityKind(hostname: string): 'IP' | 'DOMAIN' {\n return IP_REGEX.test(hostname) ? 'IP' : 'DOMAIN';\n}\n\n// ---------------------------------------------------------------------------\n// メインパーサー\n// ---------------------------------------------------------------------------\n\n/**\n * ffuf の JSON 出力文字列をパースし、ParseResult を返す。\n *\n * @param jsonContent - ffuf が `-of json` で出力した JSON 文字列\n * @returns ParseResult\n */\nexport function parseFfufJson(jsonContent: string): ParseResult {\n const raw: unknown = JSON.parse(jsonContent);\n const ffuf = validateFfufJson(raw);\n const method = ffuf.config.method;\n\n // results が空なら全て空配列\n if (ffuf.results.length === 0) {\n return emptyParseResult();\n }\n\n // ----- 集約用 Map -----\n // hosts: authority -> ParsedHost\n const hostsMap = new Map<string, ParsedHost>();\n // services: \"authority:port\" -> ParsedService\n const servicesMap = new Map<string, ParsedService>();\n // httpEndpoints: \"method:path\" -> ParsedHttpEndpoint\n const endpointsMap = new Map<string, ParsedHttpEndpoint>();\n // inputs: \"name\" -> ParsedInput (クエリパラメータ名で重複排除)\n const inputsMap = new Map<string, ParsedInput>();\n // endpointInputs: \"method:path:location:name\" -> ParsedEndpointInput\n const endpointInputsMap = new Map<string, ParsedEndpointInput>();\n // observations: \"location:name:rawValue\" -> ParsedObservation\n const observationsMap = new Map<string, ParsedObservation>();\n\n for (const result of ffuf.results) {\n if (result.url === '') {\n continue;\n }\n\n const parsed = parseUrl(result.url);\n const { scheme, hostname, port, pathname, searchParams } = parsed;\n const authorityKind = determineAuthorityKind(hostname);\n const baseUri = `${scheme}://${hostname}:${port}`;\n\n // --- Host ---\n if (!hostsMap.has(hostname)) {\n hostsMap.set(hostname, {\n authority: hostname,\n authorityKind,\n });\n }\n\n // --- Service ---\n const serviceKey = `${hostname}:${port}`;\n if (!servicesMap.has(serviceKey)) {\n servicesMap.set(serviceKey, {\n hostAuthority: hostname,\n transport: 'tcp',\n port,\n appProto: scheme,\n protoConfidence: 'high',\n state: 'open',\n });\n }\n\n // --- HTTP Endpoint (method + path で重複排除) ---\n const endpointKey = `${method}:${pathname}`;\n if (!endpointsMap.has(endpointKey)) {\n endpointsMap.set(endpointKey, {\n hostAuthority: hostname,\n port,\n baseUri,\n method,\n path: pathname,\n statusCode: result.status,\n contentLength: result.length,\n words: result.words,\n lines: result.lines,\n });\n }\n\n // --- Query Parameters -> inputs, observations, endpointInputs ---\n for (const [paramName, paramValue] of searchParams.entries()) {\n // Input (パラメータ名で重複排除)\n if (!inputsMap.has(paramName)) {\n inputsMap.set(paramName, {\n hostAuthority: hostname,\n port,\n location: 'query',\n name: paramName,\n });\n }\n\n // EndpointInput (endpoint + input の組み合わせで重複排除)\n const eiKey = `${method}:${pathname}:query:${paramName}`;\n if (!endpointInputsMap.has(eiKey)) {\n endpointInputsMap.set(eiKey, {\n hostAuthority: hostname,\n port,\n method,\n path: pathname,\n inputLocation: 'query',\n inputName: paramName,\n });\n }\n\n // Observation (パラメータ名 + 値で重複排除)\n const obsKey = `query:${paramName}:${paramValue}`;\n if (!observationsMap.has(obsKey)) {\n observationsMap.set(obsKey, {\n hostAuthority: hostname,\n port,\n inputLocation: 'query',\n inputName: paramName,\n rawValue: paramValue,\n normValue: paramValue,\n source: 'ffuf_url',\n confidence: 'high',\n });\n }\n }\n }\n\n return {\n hosts: [...hostsMap.values()],\n services: [...servicesMap.values()],\n serviceObservations: [],\n httpEndpoints: [...endpointsMap.values()],\n inputs: [...inputsMap.values()],\n endpointInputs: [...endpointInputsMap.values()],\n observations: [...observationsMap.values()],\n vulnerabilities: [],\n cves: [],\n };\n}\n","/**\n * sonobat — Nuclei JSONL パーサー\n *\n * nuclei の JSONL 出力を解析し、ParseResult 中間表現を返す。\n * 各行は独立した JSON オブジェクト(nuclei finding)として処理する。\n */\n\nimport type {\n ParseResult,\n ParsedHost,\n ParsedService,\n ParsedHttpEndpoint,\n ParsedVulnerability,\n ParsedCve,\n} from '../types/parser.js';\nimport { emptyParseResult } from '../types/parser.js';\n\n// ============================================================\n// nuclei finding の型定義(unknown から安全に取り出すための構造)\n// ============================================================\n\ninterface NucleiClassification {\n 'cve-id'?: string[];\n 'cvss-metrics'?: string;\n 'cvss-score'?: number;\n}\n\ninterface NucleiInfo {\n name: string;\n severity: string;\n tags: string[];\n classification?: NucleiClassification;\n}\n\ninterface NucleiFinding {\n 'template-id': string;\n info: NucleiInfo;\n type: string;\n host: string;\n 'matched-at': string;\n ip: string;\n port: string;\n scheme: string;\n url: string;\n}\n\n// ============================================================\n// 型ガード\n// ============================================================\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n return (\n Array.isArray(value) && value.every((item) => typeof item === 'string')\n );\n}\n\nfunction isNucleiClassification(\n value: unknown,\n): value is NucleiClassification {\n if (!isRecord(value)) return false;\n // classification は空オブジェクトでも許容する\n if ('cve-id' in value && !isStringArray(value['cve-id'])) return false;\n if (\n 'cvss-metrics' in value &&\n typeof value['cvss-metrics'] !== 'string' &&\n value['cvss-metrics'] !== undefined\n )\n return false;\n if (\n 'cvss-score' in value &&\n typeof value['cvss-score'] !== 'number' &&\n value['cvss-score'] !== undefined\n )\n return false;\n return true;\n}\n\nfunction isNucleiInfo(value: unknown): value is NucleiInfo {\n if (!isRecord(value)) return false;\n if (typeof value.name !== 'string') return false;\n if (typeof value.severity !== 'string') return false;\n if (!isStringArray(value.tags)) return false;\n if (\n 'classification' in value &&\n value.classification !== undefined &&\n !isNucleiClassification(value.classification)\n )\n return false;\n return true;\n}\n\nfunction isNucleiFinding(value: unknown): value is NucleiFinding {\n if (!isRecord(value)) return false;\n if (typeof value['template-id'] !== 'string') return false;\n if (!isNucleiInfo(value.info)) return false;\n if (typeof value.type !== 'string') return false;\n if (typeof value.host !== 'string') return false;\n if (typeof value['matched-at'] !== 'string') return false;\n if (typeof value.ip !== 'string') return false;\n if (typeof value.port !== 'string') return false;\n if (typeof value.scheme !== 'string') return false;\n if (typeof value.url !== 'string') return false;\n return true;\n}\n\n// ============================================================\n// URL パス抽出(生文字列から、デコードせずに抽出する)\n// ============================================================\n\n/**\n * URL 文字列から pathname 部分を生文字列のまま抽出する。\n * Node.js の URL クラスは %2e を . にデコードしてパスを正規化してしまうため、\n * パストラバーサル系のペイロードを保存するには raw なパスが必要。\n */\nfunction extractRawPathname(urlStr: string): string {\n // scheme://authority の後のパス部分を取り出す\n // authority の終わり = 3つ目の / の位置(scheme://host:port/path...)\n const schemeEnd = urlStr.indexOf('://');\n if (schemeEnd === -1) {\n return '/';\n }\n const afterAuthority = urlStr.indexOf('/', schemeEnd + 3);\n if (afterAuthority === -1) {\n return '/';\n }\n // パスの終わり = ? または # の最初の出現位置\n const queryStart = urlStr.indexOf('?', afterAuthority);\n const fragmentStart = urlStr.indexOf('#', afterAuthority);\n let pathEnd = urlStr.length;\n if (queryStart !== -1 && queryStart < pathEnd) {\n pathEnd = queryStart;\n }\n if (fragmentStart !== -1 && fragmentStart < pathEnd) {\n pathEnd = fragmentStart;\n }\n return urlStr.substring(afterAuthority, pathEnd);\n}\n\n// ============================================================\n// vulnType 推定\n// ============================================================\n\n/** タグ配列から vulnType を推定する。優先度順に判定する。 */\nfunction inferVulnType(tags: string[]): string {\n const priorityTags = ['sqli', 'xss', 'rce', 'lfi', 'ssrf'] as const;\n for (const tag of priorityTags) {\n if (tags.includes(tag)) {\n return tag;\n }\n }\n return 'other';\n}\n\n// ============================================================\n// メインパーサー\n// ============================================================\n\n/**\n * nuclei JSONL 出力をパースし、ParseResult を返す。\n *\n * @param jsonl - nuclei の JSONL 出力文字列(1行1JSON)\n * @returns ParseResult 中間表現\n */\nexport function parseNucleiJsonl(jsonl: string): ParseResult {\n const result = emptyParseResult();\n\n if (jsonl.trim() === '') {\n return result;\n }\n\n const lines = jsonl.split('\\n');\n const seenHosts = new Set<string>();\n const seenServices = new Set<string>();\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed === '') {\n continue;\n }\n\n const parsed: unknown = JSON.parse(trimmed);\n if (!isNucleiFinding(parsed)) {\n continue;\n }\n\n processFinding(parsed, result, seenHosts, seenServices);\n }\n\n return result;\n}\n\n/**\n * 単一の nuclei finding を処理し、結果に追加する。\n */\nfunction processFinding(\n finding: NucleiFinding,\n result: ParseResult,\n seenHosts: Set<string>,\n seenServices: Set<string>,\n): void {\n const ip = finding.ip;\n const port = Number(finding.port);\n const scheme = finding.scheme;\n const matchedAt = finding['matched-at'];\n\n // --- ホスト(重複排除) ---\n if (!seenHosts.has(ip)) {\n seenHosts.add(ip);\n const host: ParsedHost = {\n authority: ip,\n authorityKind: 'IP',\n };\n result.hosts.push(host);\n }\n\n // --- サービス(authority+port で重複排除) ---\n const serviceKey = `${ip}:${port}`;\n if (!seenServices.has(serviceKey)) {\n seenServices.add(serviceKey);\n const service: ParsedService = {\n hostAuthority: ip,\n transport: 'tcp',\n port,\n appProto: scheme,\n protoConfidence: 'high',\n state: 'open',\n };\n result.services.push(service);\n }\n\n // --- HTTP エンドポイント ---\n const rawPath = extractRawPathname(matchedAt);\n const baseUri = `${scheme}://${ip}:${port}`;\n\n const endpoint: ParsedHttpEndpoint = {\n hostAuthority: ip,\n port,\n baseUri,\n method: 'GET',\n path: rawPath,\n };\n result.httpEndpoints.push(endpoint);\n\n // --- 脆弱性 ---\n const info = finding.info;\n const vulnerability: ParsedVulnerability = {\n hostAuthority: ip,\n port,\n method: 'GET',\n path: rawPath,\n vulnType: inferVulnType(info.tags),\n title: info.name,\n severity: info.severity,\n confidence: 'high',\n };\n result.vulnerabilities.push(vulnerability);\n\n // --- CVE ---\n const classification = info.classification;\n if (\n classification !== undefined &&\n isRecord(classification) &&\n 'cve-id' in classification &&\n isStringArray(classification['cve-id']) &&\n classification['cve-id'].length > 0\n ) {\n for (const cveId of classification['cve-id']) {\n const cve: ParsedCve = {\n vulnerabilityTitle: info.name,\n cveId,\n cvssScore: classification['cvss-score'],\n cvssVector: classification['cvss-metrics'],\n };\n result.cves.push(cve);\n }\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { ServiceObservation } from '../../types/entities.js';\nimport type { CreateServiceObservationInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `service_observations` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface ServiceObservationRow {\n id: string;\n service_id: string;\n key: string;\n value: string;\n confidence: string;\n evidence_artifact_id: string;\n created_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase ServiceObservation entity. */\nfunction rowToServiceObservation(row: ServiceObservationRow): ServiceObservation {\n return {\n id: row.id,\n serviceId: row.service_id,\n key: row.key,\n value: row.value,\n confidence: row.confidence,\n evidenceArtifactId: row.evidence_artifact_id,\n createdAt: row.created_at,\n };\n}\n\n/**\n * Repository for the `service_observations` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class ServiceObservationRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new ServiceObservation and return the full entity. */\n create(input: CreateServiceObservationInput): ServiceObservation {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string, string, string, string, string]\n >(\n `INSERT INTO service_observations (id, service_id, key, value, confidence, evidence_artifact_id, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.serviceId,\n input.key,\n input.value,\n input.confidence,\n input.evidenceArtifactId,\n now,\n );\n\n return {\n id,\n serviceId: input.serviceId,\n key: input.key,\n value: input.value,\n confidence: input.confidence,\n evidenceArtifactId: input.evidenceArtifactId,\n createdAt: now,\n };\n }\n\n /** Find a ServiceObservation by its primary key. Returns undefined if not found. */\n findById(id: string): ServiceObservation | undefined {\n const stmt = this.db.prepare<[string], ServiceObservationRow>(\n `SELECT id, service_id, key, value, confidence, evidence_artifact_id, created_at\n FROM service_observations\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToServiceObservation(row) : undefined;\n }\n\n /** Return all ServiceObservations for a given service. */\n findByServiceId(serviceId: string): ServiceObservation[] {\n const stmt = this.db.prepare<[string], ServiceObservationRow>(\n `SELECT id, service_id, key, value, confidence, evidence_artifact_id, created_at\n FROM service_observations\n WHERE service_id = ?`,\n );\n\n const rows = stmt.all(serviceId);\n return rows.map(rowToServiceObservation);\n }\n\n /** Delete a ServiceObservation by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM service_observations WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { EndpointInput } from '../../types/entities.js';\nimport type { CreateEndpointInputInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `endpoint_inputs` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface EndpointInputRow {\n id: string;\n endpoint_id: string;\n input_id: string;\n evidence_artifact_id: string;\n created_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase EndpointInput entity. */\nfunction rowToEndpointInput(row: EndpointInputRow): EndpointInput {\n return {\n id: row.id,\n endpointId: row.endpoint_id,\n inputId: row.input_id,\n evidenceArtifactId: row.evidence_artifact_id,\n createdAt: row.created_at,\n };\n}\n\n/**\n * Repository for the `endpoint_inputs` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class EndpointInputRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new EndpointInput and return the full entity. */\n create(input: CreateEndpointInputInput): EndpointInput {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string, string, string]\n >(\n `INSERT INTO endpoint_inputs (id, endpoint_id, input_id, evidence_artifact_id, created_at)\n VALUES (?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.endpointId,\n input.inputId,\n input.evidenceArtifactId,\n now,\n );\n\n return {\n id,\n endpointId: input.endpointId,\n inputId: input.inputId,\n evidenceArtifactId: input.evidenceArtifactId,\n createdAt: now,\n };\n }\n\n /** Find an EndpointInput by its primary key. Returns undefined if not found. */\n findById(id: string): EndpointInput | undefined {\n const stmt = this.db.prepare<[string], EndpointInputRow>(\n `SELECT id, endpoint_id, input_id, evidence_artifact_id, created_at\n FROM endpoint_inputs\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToEndpointInput(row) : undefined;\n }\n\n /** Find all EndpointInputs for a given endpoint. */\n findByEndpointId(endpointId: string): EndpointInput[] {\n const stmt = this.db.prepare<[string], EndpointInputRow>(\n `SELECT id, endpoint_id, input_id, evidence_artifact_id, created_at\n FROM endpoint_inputs\n WHERE endpoint_id = ?`,\n );\n\n const rows = stmt.all(endpointId);\n return rows.map(rowToEndpointInput);\n }\n\n /** Find all EndpointInputs for a given input. */\n findByInputId(inputId: string): EndpointInput[] {\n const stmt = this.db.prepare<[string], EndpointInputRow>(\n `SELECT id, endpoint_id, input_id, evidence_artifact_id, created_at\n FROM endpoint_inputs\n WHERE input_id = ?`,\n );\n\n const rows = stmt.all(inputId);\n return rows.map(rowToEndpointInput);\n }\n\n /** Delete an EndpointInput by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM endpoint_inputs WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Cve } from '../../types/entities.js';\nimport type { CreateCveInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `cves` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface CveRow {\n id: string;\n vulnerability_id: string;\n cve_id: string;\n description: string | null;\n cvss_score: number | null;\n cvss_vector: string | null;\n reference_url: string | null;\n created_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Cve entity. */\nfunction rowToCve(row: CveRow): Cve {\n return {\n id: row.id,\n vulnerabilityId: row.vulnerability_id,\n cveId: row.cve_id,\n description: row.description ?? undefined,\n cvssScore: row.cvss_score ?? undefined,\n cvssVector: row.cvss_vector ?? undefined,\n referenceUrl: row.reference_url ?? undefined,\n createdAt: row.created_at,\n };\n}\n\n/**\n * Repository for the `cves` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class CveRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Cve and return the full entity. */\n create(input: CreateCveInput): Cve {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string, string | null, number | null, string | null, string | null, string]\n >(\n `INSERT INTO cves (id, vulnerability_id, cve_id, description, cvss_score, cvss_vector, reference_url, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.vulnerabilityId,\n input.cveId,\n input.description ?? null,\n input.cvssScore ?? null,\n input.cvssVector ?? null,\n input.referenceUrl ?? null,\n now,\n );\n\n return {\n id,\n vulnerabilityId: input.vulnerabilityId,\n cveId: input.cveId,\n description: input.description,\n cvssScore: input.cvssScore,\n cvssVector: input.cvssVector,\n referenceUrl: input.referenceUrl,\n createdAt: now,\n };\n }\n\n /** Find a Cve by its primary key. Returns undefined if not found. */\n findById(id: string): Cve | undefined {\n const stmt = this.db.prepare<[string], CveRow>(\n `SELECT id, vulnerability_id, cve_id, description, cvss_score, cvss_vector, reference_url, created_at\n FROM cves\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToCve(row) : undefined;\n }\n\n /** Return all Cves associated with a given vulnerability. */\n findByVulnerabilityId(vulnerabilityId: string): Cve[] {\n const stmt = this.db.prepare<[string], CveRow>(\n `SELECT id, vulnerability_id, cve_id, description, cvss_score, cvss_vector, reference_url, created_at\n FROM cves\n WHERE vulnerability_id = ?`,\n );\n\n const rows = stmt.all(vulnerabilityId);\n return rows.map(rowToCve);\n }\n\n /** Delete a Cve by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM cves WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","/**\n * sonobat — Normalizer\n *\n * ParseResult(パーサーの中間表現)を受け取り、\n * 自然キーで既存レコードを検索(upsert)しながら DB に書き込む。\n * 全操作はトランザクションでラップし、アトミックに実行する。\n */\n\nimport type Database from 'better-sqlite3';\nimport type { ParseResult } from '../types/parser.js';\nimport { HostRepository } from '../db/repository/host-repository.js';\nimport { ServiceRepository } from '../db/repository/service-repository.js';\nimport { ServiceObservationRepository } from '../db/repository/service-observation-repository.js';\nimport { HttpEndpointRepository } from '../db/repository/http-endpoint-repository.js';\nimport { InputRepository } from '../db/repository/input-repository.js';\nimport { EndpointInputRepository } from '../db/repository/endpoint-input-repository.js';\nimport { ObservationRepository } from '../db/repository/observation-repository.js';\nimport { VulnerabilityRepository } from '../db/repository/vulnerability-repository.js';\nimport { CveRepository } from '../db/repository/cve-repository.js';\n\n// ============================================================\n// 結果型\n// ============================================================\n\n/** normalize() の実行結果。各エンティティの新規作成数を返す。 */\nexport interface NormalizeResult {\n hostsCreated: number;\n servicesCreated: number;\n serviceObservationsCreated: number;\n httpEndpointsCreated: number;\n inputsCreated: number;\n endpointInputsCreated: number;\n observationsCreated: number;\n vulnerabilitiesCreated: number;\n cvesCreated: number;\n}\n\n// ============================================================\n// normalize\n// ============================================================\n\n/**\n * ParseResult を DB に正規化して書き込む。\n *\n * - 自然キー(authority, host_id+transport+port 等)で既存レコードを検索し、\n * 存在すれば再利用、なければ新規作成する(upsert パターン)。\n * - 全操作は 1 トランザクション内で実行される。\n *\n * @param db better-sqlite3 の Database インスタンス\n * @param artifactId 根拠となる Artifact の ID(evidence_artifact_id に設定)\n * @param parseResult パーサーが返した中間表現\n * @returns 各エンティティの新規作成数\n */\nexport function normalize(\n db: Database.Database,\n artifactId: string,\n parseResult: ParseResult,\n): NormalizeResult {\n const hostRepo = new HostRepository(db);\n const serviceRepo = new ServiceRepository(db);\n const serviceObsRepo = new ServiceObservationRepository(db);\n const httpEndpointRepo = new HttpEndpointRepository(db);\n const inputRepo = new InputRepository(db);\n const endpointInputRepo = new EndpointInputRepository(db);\n const observationRepo = new ObservationRepository(db);\n const vulnRepo = new VulnerabilityRepository(db);\n const cveRepo = new CveRepository(db);\n\n const run = db.transaction((): NormalizeResult => {\n const result: NormalizeResult = {\n hostsCreated: 0,\n servicesCreated: 0,\n serviceObservationsCreated: 0,\n httpEndpointsCreated: 0,\n inputsCreated: 0,\n endpointInputsCreated: 0,\n observationsCreated: 0,\n vulnerabilitiesCreated: 0,\n cvesCreated: 0,\n };\n\n // ---------------------------------------------------------\n // 自然キー → DB ID のマッピング\n // ---------------------------------------------------------\n const hostIdByAuthority = new Map<string, string>();\n // key: \"hostId:transport:port\"\n const serviceIdByKey = new Map<string, string>();\n // key: \"serviceId:method:path\"\n const endpointIdByKey = new Map<string, string>();\n // key: \"serviceId:location:name\"\n const inputIdByKey = new Map<string, string>();\n // key: vulnerability title → DB ID\n const vulnIdByTitle = new Map<string, string>();\n\n // ---------------------------------------------------------\n // 1. Upsert hosts\n // ---------------------------------------------------------\n for (const parsed of parseResult.hosts) {\n const existing = hostRepo.findByAuthority(parsed.authority);\n if (existing) {\n hostIdByAuthority.set(parsed.authority, existing.id);\n } else {\n const host = hostRepo.create({\n authorityKind: parsed.authorityKind,\n authority: parsed.authority,\n resolvedIpsJson: JSON.stringify(parsed.resolvedIps ?? []),\n });\n hostIdByAuthority.set(parsed.authority, host.id);\n result.hostsCreated++;\n }\n }\n\n // ---------------------------------------------------------\n // 2. Upsert services\n // ---------------------------------------------------------\n for (const parsed of parseResult.services) {\n const hostId = hostIdByAuthority.get(parsed.hostAuthority);\n if (!hostId) continue;\n\n const svcKey = `${hostId}:${parsed.transport}:${parsed.port}`;\n if (serviceIdByKey.has(svcKey)) continue;\n\n // DB から既存サービスを検索\n const existingServices = serviceRepo.findByHostId(hostId);\n const existing = existingServices.find(\n (s) => s.transport === parsed.transport && s.port === parsed.port,\n );\n\n if (existing) {\n serviceIdByKey.set(svcKey, existing.id);\n } else {\n const service = serviceRepo.create({\n hostId,\n transport: parsed.transport,\n port: parsed.port,\n appProto: parsed.appProto,\n protoConfidence: parsed.protoConfidence,\n banner: parsed.banner,\n product: parsed.product,\n version: parsed.version,\n state: parsed.state,\n evidenceArtifactId: artifactId,\n });\n serviceIdByKey.set(svcKey, service.id);\n result.servicesCreated++;\n }\n }\n\n // ---------------------------------------------------------\n // ヘルパー: hostAuthority + port → serviceId を解決\n // HTTP 系エンティティは transport が常に tcp\n // ---------------------------------------------------------\n function resolveServiceId(hostAuthority: string, port: number): string | undefined {\n const hostId = hostIdByAuthority.get(hostAuthority);\n if (!hostId) return undefined;\n return serviceIdByKey.get(`${hostId}:tcp:${port}`);\n }\n\n // ---------------------------------------------------------\n // 3. Service observations\n // ---------------------------------------------------------\n for (const parsed of parseResult.serviceObservations) {\n const hostId = hostIdByAuthority.get(parsed.hostAuthority);\n if (!hostId) continue;\n\n const svcKey = `${hostId}:${parsed.transport}:${parsed.port}`;\n const serviceId = serviceIdByKey.get(svcKey);\n if (!serviceId) continue;\n\n serviceObsRepo.create({\n serviceId,\n key: parsed.key,\n value: parsed.value,\n confidence: parsed.confidence,\n evidenceArtifactId: artifactId,\n });\n result.serviceObservationsCreated++;\n }\n\n // ---------------------------------------------------------\n // 4. Upsert HTTP endpoints\n // ---------------------------------------------------------\n for (const parsed of parseResult.httpEndpoints) {\n const serviceId = resolveServiceId(parsed.hostAuthority, parsed.port);\n if (!serviceId) continue;\n\n const epKey = `${serviceId}:${parsed.method}:${parsed.path}`;\n if (endpointIdByKey.has(epKey)) continue;\n\n const existingEndpoints = httpEndpointRepo.findByServiceId(serviceId);\n const existing = existingEndpoints.find(\n (e) => e.method === parsed.method && e.path === parsed.path,\n );\n\n if (existing) {\n endpointIdByKey.set(epKey, existing.id);\n } else {\n const endpoint = httpEndpointRepo.create({\n serviceId,\n baseUri: parsed.baseUri,\n method: parsed.method,\n path: parsed.path,\n statusCode: parsed.statusCode,\n contentLength: parsed.contentLength,\n words: parsed.words,\n lines: parsed.lines,\n evidenceArtifactId: artifactId,\n });\n endpointIdByKey.set(epKey, endpoint.id);\n result.httpEndpointsCreated++;\n }\n }\n\n // ---------------------------------------------------------\n // 5. Upsert inputs\n // ---------------------------------------------------------\n for (const parsed of parseResult.inputs) {\n const serviceId = resolveServiceId(parsed.hostAuthority, parsed.port);\n if (!serviceId) continue;\n\n const inKey = `${serviceId}:${parsed.location}:${parsed.name}`;\n if (inputIdByKey.has(inKey)) continue;\n\n const existingInputs = inputRepo.findByServiceId(serviceId);\n const existing = existingInputs.find(\n (i) => i.location === parsed.location && i.name === parsed.name,\n );\n\n if (existing) {\n inputIdByKey.set(inKey, existing.id);\n } else {\n const input = inputRepo.create({\n serviceId,\n location: parsed.location,\n name: parsed.name,\n typeHint: parsed.typeHint,\n });\n inputIdByKey.set(inKey, input.id);\n result.inputsCreated++;\n }\n }\n\n // ---------------------------------------------------------\n // 6. Upsert endpoint_inputs\n // ---------------------------------------------------------\n for (const parsed of parseResult.endpointInputs) {\n const serviceId = resolveServiceId(parsed.hostAuthority, parsed.port);\n if (!serviceId) continue;\n\n const epKey = `${serviceId}:${parsed.method}:${parsed.path}`;\n const endpointId = endpointIdByKey.get(epKey);\n if (!endpointId) continue;\n\n const inKey = `${serviceId}:${parsed.inputLocation}:${parsed.inputName}`;\n const inputId = inputIdByKey.get(inKey);\n if (!inputId) continue;\n\n // 既存リンクの確認\n const existingLinks = endpointInputRepo.findByEndpointId(endpointId);\n const alreadyLinked = existingLinks.some((l) => l.inputId === inputId);\n if (alreadyLinked) continue;\n\n endpointInputRepo.create({\n endpointId,\n inputId,\n evidenceArtifactId: artifactId,\n });\n result.endpointInputsCreated++;\n }\n\n // ---------------------------------------------------------\n // 7. Observations(常に新規作成)\n // ---------------------------------------------------------\n for (const parsed of parseResult.observations) {\n const serviceId = resolveServiceId(parsed.hostAuthority, parsed.port);\n if (!serviceId) continue;\n\n const inKey = `${serviceId}:${parsed.inputLocation}:${parsed.inputName}`;\n const inputId = inputIdByKey.get(inKey);\n if (!inputId) continue;\n\n observationRepo.create({\n inputId,\n rawValue: parsed.rawValue,\n normValue: parsed.normValue,\n source: parsed.source,\n confidence: parsed.confidence,\n evidenceArtifactId: artifactId,\n observedAt: new Date().toISOString(),\n });\n result.observationsCreated++;\n }\n\n // ---------------------------------------------------------\n // 8. Vulnerabilities\n // ---------------------------------------------------------\n for (const parsed of parseResult.vulnerabilities) {\n const serviceId = resolveServiceId(parsed.hostAuthority, parsed.port);\n if (!serviceId) continue;\n\n // endpoint への紐づけ(任意)\n let endpointId: string | undefined;\n if (parsed.method && parsed.path) {\n const epKey = `${serviceId}:${parsed.method}:${parsed.path}`;\n endpointId = endpointIdByKey.get(epKey);\n }\n\n const vuln = vulnRepo.create({\n serviceId,\n endpointId,\n vulnType: parsed.vulnType,\n title: parsed.title,\n description: parsed.description,\n severity: parsed.severity,\n confidence: parsed.confidence,\n evidenceArtifactId: artifactId,\n });\n vulnIdByTitle.set(parsed.title, vuln.id);\n result.vulnerabilitiesCreated++;\n }\n\n // ---------------------------------------------------------\n // 9. CVEs\n // ---------------------------------------------------------\n for (const parsed of parseResult.cves) {\n const vulnId = vulnIdByTitle.get(parsed.vulnerabilityTitle);\n if (!vulnId) continue;\n\n cveRepo.create({\n vulnerabilityId: vulnId,\n cveId: parsed.cveId,\n description: parsed.description,\n cvssScore: parsed.cvssScore,\n cvssVector: parsed.cvssVector,\n referenceUrl: parsed.referenceUrl,\n });\n result.cvesCreated++;\n }\n\n return result;\n });\n\n return run();\n}\n","/**\n * sonobat — MCP Propose Tool\n *\n * Tool for generating next-step action proposals based on missing data.\n */\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type Database from 'better-sqlite3';\nimport { z } from 'zod';\nimport { propose } from '../../engine/proposer.js';\n\nexport function registerProposeTool(server: McpServer, db: Database.Database): void {\n server.tool(\n 'propose',\n 'Analyze the AttackDataGraph for missing data and propose next-step actions',\n {\n hostId: z.string().optional().describe('Limit proposals to a specific host (optional)'),\n },\n async ({ hostId }) => {\n const actions = propose(db, hostId);\n if (actions.length === 0) {\n return {\n content: [{ type: 'text', text: 'No actions proposed. All discovered data appears complete.' }],\n };\n }\n return { content: [{ type: 'text', text: JSON.stringify(actions, null, 2) }] };\n },\n );\n}\n","/**\n * sonobat — Proposer engine\n *\n * Analyzes the AttackDataGraph stored in SQLite and proposes\n * next-step actions (scans, discovery, etc.) based on missing data.\n */\n\nimport type Database from 'better-sqlite3';\nimport type { Action } from '../types/engine.js';\nimport { HostRepository } from '../db/repository/host-repository.js';\nimport { ServiceRepository } from '../db/repository/service-repository.js';\nimport { HttpEndpointRepository } from '../db/repository/http-endpoint-repository.js';\nimport { InputRepository } from '../db/repository/input-repository.js';\nimport { ObservationRepository } from '../db/repository/observation-repository.js';\nimport { VhostRepository } from '../db/repository/vhost-repository.js';\nimport { VulnerabilityRepository } from '../db/repository/vulnerability-repository.js';\nimport type { Host, Service } from '../types/entities.js';\n\n/**\n * Analyze the database for missing reconnaissance data and return\n * a list of proposed actions to fill the gaps.\n *\n * @param db - The better-sqlite3 database instance\n * @param hostId - Optional: limit analysis to a single host\n * @returns Array of proposed actions\n */\nexport function propose(db: Database.Database, hostId?: string): Action[] {\n const hostRepo = new HostRepository(db);\n const serviceRepo = new ServiceRepository(db);\n const httpEndpointRepo = new HttpEndpointRepository(db);\n const inputRepo = new InputRepository(db);\n const observationRepo = new ObservationRepository(db);\n const vhostRepo = new VhostRepository(db);\n const vulnRepo = new VulnerabilityRepository(db);\n\n const actions: Action[] = [];\n\n // Determine target hosts\n let hosts: Host[];\n if (hostId !== undefined) {\n const host = hostRepo.findById(hostId);\n if (host === undefined) {\n return [];\n }\n hosts = [host];\n } else {\n hosts = hostRepo.findAll();\n }\n\n for (const host of hosts) {\n const services = serviceRepo.findByHostId(host.id);\n\n // (a) No services at all -> suggest nmap scan\n if (services.length === 0) {\n actions.push({\n kind: 'nmap_scan',\n description: `Port scan ${host.authority} to discover services`,\n command: `nmap -p- -sV ${host.authority}`,\n params: { hostId: host.id },\n });\n continue;\n }\n\n // (b) For each HTTP/HTTPS service, check for missing data\n for (const service of services) {\n if (service.appProto !== 'http' && service.appProto !== 'https') {\n continue;\n }\n\n const baseUri = `${service.appProto}://${host.authority}:${service.port}`;\n\n proposeForHttpService(\n actions,\n host,\n service,\n baseUri,\n httpEndpointRepo,\n inputRepo,\n observationRepo,\n vhostRepo,\n vulnRepo,\n );\n }\n }\n\n return actions;\n}\n\n/**\n * Check a single HTTP/HTTPS service for missing data and push\n * proposed actions into the actions array.\n */\nfunction proposeForHttpService(\n actions: Action[],\n host: Host,\n service: Service,\n baseUri: string,\n httpEndpointRepo: HttpEndpointRepository,\n inputRepo: InputRepository,\n observationRepo: ObservationRepository,\n vhostRepo: VhostRepository,\n vulnRepo: VulnerabilityRepository,\n): void {\n const endpoints = httpEndpointRepo.findByServiceId(service.id);\n\n // No endpoints -> suggest directory/file discovery\n if (endpoints.length === 0) {\n actions.push({\n kind: 'ffuf_discovery',\n description: `Discover endpoints on ${baseUri}`,\n command: `ffuf -u ${baseUri}/FUZZ -w /usr/share/wordlists/dirb/common.txt`,\n params: { hostId: host.id, serviceId: service.id },\n });\n }\n\n // For each endpoint: check inputs\n for (const endpoint of endpoints) {\n const inputs = inputRepo.findByServiceId(service.id);\n\n if (inputs.length === 0) {\n actions.push({\n kind: 'parameter_discovery',\n description: `Discover input parameters for ${baseUri}${endpoint.path}`,\n params: { hostId: host.id, serviceId: service.id, endpointId: endpoint.id },\n });\n }\n\n // For each input: check observations\n for (const input of inputs) {\n const observations = observationRepo.findByInputId(input.id);\n\n if (observations.length === 0) {\n actions.push({\n kind: 'value_collection',\n description: `Collect observed values for input \"${input.name}\" (${input.location})`,\n params: {\n hostId: host.id,\n serviceId: service.id,\n endpointId: endpoint.id,\n inputId: input.id,\n },\n });\n }\n }\n }\n\n // Check vhosts\n const vhosts = vhostRepo.findByHostId(host.id);\n if (vhosts.length === 0) {\n actions.push({\n kind: 'vhost_discovery',\n description: `Discover virtual hosts for ${host.authority}`,\n params: { hostId: host.id, serviceId: service.id },\n });\n }\n\n // Check vulnerabilities\n const vulns = vulnRepo.findByServiceId(service.id);\n if (vulns.length === 0) {\n actions.push({\n kind: 'nuclei_scan',\n description: `Scan ${baseUri} for known vulnerabilities`,\n command: `nuclei -u ${baseUri} -jsonl`,\n params: { hostId: host.id, serviceId: service.id },\n });\n }\n}\n","/**\n * sonobat — MCP Mutation Tools\n *\n * Tools for manually adding data to the AttackDataGraph.\n */\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type Database from 'better-sqlite3';\nimport { z } from 'zod';\nimport { HostRepository } from '../../db/repository/host-repository.js';\nimport { ArtifactRepository } from '../../db/repository/artifact-repository.js';\nimport { CredentialRepository } from '../../db/repository/credential-repository.js';\nimport { VulnerabilityRepository } from '../../db/repository/vulnerability-repository.js';\nimport { CveRepository } from '../../db/repository/cve-repository.js';\n\n/**\n * Get or create a singleton \"manual\" artifact for manual data entry.\n * Reused across all manual mutations to avoid artifact proliferation.\n */\nfunction getOrCreateManualArtifact(db: Database.Database): string {\n const artifactRepo = new ArtifactRepository(db);\n const existing = artifactRepo.findByTool('manual');\n if (existing.length > 0) {\n return existing[0].id;\n }\n const artifact = artifactRepo.create({\n tool: 'manual',\n kind: 'manual_entry',\n path: 'manual',\n capturedAt: new Date().toISOString(),\n });\n return artifact.id;\n}\n\nexport function registerMutationTools(server: McpServer, db: Database.Database): void {\n // 1. add_host\n server.tool(\n 'add_host',\n 'Manually add a host to the AttackDataGraph',\n {\n authority: z.string().describe('IP address or domain name'),\n authorityKind: z.enum(['IP', 'DOMAIN']).describe('Type of authority'),\n },\n async ({ authority, authorityKind }) => {\n const hostRepo = new HostRepository(db);\n const existing = hostRepo.findByAuthority(authority);\n if (existing) {\n return {\n content: [{ type: 'text', text: `Host already exists: ${JSON.stringify(existing, null, 2)}` }],\n };\n }\n const host = hostRepo.create({\n authorityKind,\n authority,\n resolvedIpsJson: '[]',\n });\n return { content: [{ type: 'text', text: JSON.stringify(host, null, 2) }] };\n },\n );\n\n // 2. add_credential\n server.tool(\n 'add_credential',\n 'Manually add a credential for a service',\n {\n serviceId: z.string().describe('Service UUID'),\n username: z.string().describe('Username'),\n secret: z.string().describe('Secret value (password, token, etc.)'),\n secretType: z.enum(['password', 'token', 'api_key', 'ssh_key']).describe('Type of secret'),\n source: z.enum(['brute_force', 'default', 'leaked', 'manual']).describe('How the credential was obtained'),\n confidence: z.enum(['high', 'medium', 'low']).describe('Confidence level').default('medium'),\n },\n async ({ serviceId, username, secret, secretType, source, confidence }) => {\n const artifactId = getOrCreateManualArtifact(db);\n const credentialRepo = new CredentialRepository(db);\n const credential = credentialRepo.create({\n serviceId,\n username,\n secret,\n secretType,\n source,\n confidence,\n evidenceArtifactId: artifactId,\n });\n return { content: [{ type: 'text', text: JSON.stringify(credential, null, 2) }] };\n },\n );\n\n // 3. add_vulnerability\n server.tool(\n 'add_vulnerability',\n 'Manually add a vulnerability for a service',\n {\n serviceId: z.string().describe('Service UUID'),\n vulnType: z.string().describe('Vulnerability type (sqli, xss, rce, lfi, ssrf, etc.)'),\n title: z.string().describe('Vulnerability title'),\n severity: z.enum(['critical', 'high', 'medium', 'low', 'info']).describe('Severity level'),\n confidence: z.enum(['high', 'medium', 'low']).describe('Confidence level').default('medium'),\n endpointId: z.string().optional().describe('HTTP endpoint UUID (optional)'),\n description: z.string().optional().describe('Detailed description (optional)'),\n },\n async ({ serviceId, vulnType, title, severity, confidence, endpointId, description }) => {\n const artifactId = getOrCreateManualArtifact(db);\n const vulnRepo = new VulnerabilityRepository(db);\n const vuln = vulnRepo.create({\n serviceId,\n endpointId,\n vulnType,\n title,\n description,\n severity,\n confidence,\n evidenceArtifactId: artifactId,\n });\n return { content: [{ type: 'text', text: JSON.stringify(vuln, null, 2) }] };\n },\n );\n\n // 4. link_cve\n server.tool(\n 'link_cve',\n 'Link a CVE record to an existing vulnerability',\n {\n vulnerabilityId: z.string().describe('Vulnerability UUID'),\n cveId: z.string().describe('CVE identifier (e.g. CVE-2021-44228)'),\n description: z.string().optional().describe('CVE description'),\n cvssScore: z.number().optional().describe('CVSS score (0.0 - 10.0)'),\n cvssVector: z.string().optional().describe('CVSS vector string'),\n referenceUrl: z.string().optional().describe('Reference URL'),\n },\n async ({ vulnerabilityId, cveId, description, cvssScore, cvssVector, referenceUrl }) => {\n const cveRepo = new CveRepository(db);\n const cve = cveRepo.create({\n vulnerabilityId,\n cveId,\n description,\n cvssScore,\n cvssVector,\n referenceUrl,\n });\n return { content: [{ type: 'text', text: JSON.stringify(cve, null, 2) }] };\n },\n );\n}\n","/**\n * sonobat — MCP Resources\n *\n * Read-only resources for browsing the AttackDataGraph.\n */\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type Database from 'better-sqlite3';\nimport { HostRepository } from '../db/repository/host-repository.js';\nimport { ServiceRepository } from '../db/repository/service-repository.js';\nimport { VhostRepository } from '../db/repository/vhost-repository.js';\nimport { HttpEndpointRepository } from '../db/repository/http-endpoint-repository.js';\nimport { InputRepository } from '../db/repository/input-repository.js';\nimport { VulnerabilityRepository } from '../db/repository/vulnerability-repository.js';\n\nexport function registerResources(server: McpServer, db: Database.Database): void {\n const hostRepo = new HostRepository(db);\n const serviceRepo = new ServiceRepository(db);\n const vhostRepo = new VhostRepository(db);\n const httpEndpointRepo = new HttpEndpointRepository(db);\n const inputRepo = new InputRepository(db);\n const vulnRepo = new VulnerabilityRepository(db);\n\n // 1. sonobat://hosts — Host list\n server.resource('hosts', 'sonobat://hosts', { description: 'List of all discovered hosts' }, async () => {\n const hosts = hostRepo.findAll();\n return {\n contents: [\n {\n uri: 'sonobat://hosts',\n mimeType: 'application/json',\n text: JSON.stringify(hosts, null, 2),\n },\n ],\n };\n });\n\n // 2. sonobat://hosts/{id} — Host detail tree\n server.resource(\n 'host-detail',\n 'sonobat://hosts/{id}',\n { description: 'Detailed host tree with services, endpoints, inputs, and vulnerabilities' },\n async (uri) => {\n // Extract host ID from the URI\n const hostId = uri.pathname.split('/').pop() ?? '';\n const host = hostRepo.findById(hostId);\n if (!host) {\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: 'application/json',\n text: JSON.stringify({ error: `Host not found: ${hostId}` }),\n },\n ],\n };\n }\n\n const services = serviceRepo.findByHostId(hostId);\n const vhosts = vhostRepo.findByHostId(hostId);\n\n const serviceTree = services.map((service) => {\n const endpoints = httpEndpointRepo.findByServiceId(service.id);\n const inputs = inputRepo.findByServiceId(service.id);\n const vulnerabilities = vulnRepo.findByServiceId(service.id);\n return { ...service, endpoints, inputs, vulnerabilities };\n });\n\n const result = { ...host, services: serviceTree, vhosts };\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: 'application/json',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n },\n );\n\n // 3. sonobat://summary — Statistics summary\n server.resource(\n 'summary',\n 'sonobat://summary',\n { description: 'Summary statistics of the AttackDataGraph' },\n async () => {\n // Use direct SQL COUNT queries for efficiency\n const counts: Record<string, number> = {};\n const tables = [\n 'hosts',\n 'services',\n 'http_endpoints',\n 'inputs',\n 'observations',\n 'credentials',\n 'vulnerabilities',\n 'cves',\n 'vhosts',\n 'artifacts',\n ];\n\n for (const table of tables) {\n const row = db.prepare(`SELECT COUNT(*) as count FROM ${table}`).get() as { count: number };\n counts[table] = row.count;\n }\n\n return {\n contents: [\n {\n uri: 'sonobat://summary',\n mimeType: 'application/json',\n text: JSON.stringify(counts, null, 2),\n },\n ],\n };\n },\n );\n}\n"],"mappings":";;;AAOA,OAAO,cAAc;AACrB,SAAS,4BAA4B;;;ACD9B,IAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["db","db","crypto","db","crypto","db","crypto","db","crypto","db","stmt","rows","crypto","db","crypto","db","crypto","db","stmt","rows","server","db","z","crypto","crypto","db","transport","isRecord","crypto","db","crypto","db","crypto","db","db","db","crypto","server","db","z","path","z","db","server","db","z","z","db","server","z","server","db","db","server"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/db/schema.ts","../src/db/migrate.ts","../src/mcp/server.ts","../src/mcp/tools/query.ts","../src/db/repository/host-repository.ts","../src/db/repository/service-repository.ts","../src/db/repository/vhost-repository.ts","../src/db/repository/http-endpoint-repository.ts","../src/db/repository/input-repository.ts","../src/db/repository/observation-repository.ts","../src/db/repository/credential-repository.ts","../src/db/repository/vulnerability-repository.ts","../src/mcp/tools/ingest.ts","../src/engine/ingest.ts","../src/db/repository/artifact-repository.ts","../src/parser/nmap-parser.ts","../src/types/parser.ts","../src/parser/ffuf-parser.ts","../src/parser/nuclei-parser.ts","../src/db/repository/service-observation-repository.ts","../src/db/repository/endpoint-input-repository.ts","../src/db/repository/cve-repository.ts","../src/engine/normalizer.ts","../src/mcp/tools/propose.ts","../src/engine/proposer.ts","../src/mcp/tools/mutation.ts","../src/mcp/tools/datalog.ts","../src/engine/datalog/types.ts","../src/engine/datalog/tokenizer.ts","../src/engine/datalog/parser.ts","../src/engine/datalog/evaluator.ts","../src/engine/datalog/fact-extractor.ts","../src/engine/datalog/preset-rules.ts","../src/db/repository/datalog-rule-repository.ts","../src/engine/datalog/index.ts","../src/mcp/resources.ts"],"sourcesContent":["/**\n * sonobat — AttackDataGraph for autonomous penetration testing\n *\n * MCP Server エントリポイント。\n * stdio トランスポートで LLM Agent と接続する。\n */\n\nimport Database from 'better-sqlite3';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { migrateDatabase } from './db/migrate.js';\nimport { createMcpServer } from './mcp/server.js';\n\nconst DB_PATH = process.env['SONOBAT_DB_PATH'] ?? 'sonobat.db';\nconst db = new Database(DB_PATH);\nmigrateDatabase(db);\n\nconst server = createMcpServer(db);\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","/**\n * sonobat — AttackDataGraph SQLite schema\n *\n * This schema is the single source of truth for the database structure.\n * It is derived from docs/architecture.md Section 4.\n */\n\nexport const SCHEMA_SQL = `\nPRAGMA foreign_keys = ON;\n\n-- ============================================================\n-- 実行単位(任意)\n-- ============================================================\nCREATE TABLE IF NOT EXISTS scans (\n id TEXT PRIMARY KEY,\n started_at TEXT NOT NULL,\n finished_at TEXT,\n notes TEXT\n);\n\n-- ============================================================\n-- 生出力(ファイル)参照\n-- ============================================================\nCREATE TABLE IF NOT EXISTS artifacts (\n id TEXT PRIMARY KEY,\n scan_id TEXT,\n tool TEXT NOT NULL, -- \"nmap\" | \"ffuf\" | \"nuclei\"\n kind TEXT NOT NULL, -- \"tool_output\" | \"http_request\" | \"http_response\"\n path TEXT NOT NULL, -- ローカルファイルパス or URI\n sha256 TEXT,\n captured_at TEXT NOT NULL,\n attrs_json TEXT, -- 任意メタ(コマンド、引数等)\n FOREIGN KEY (scan_id) REFERENCES scans(id) ON DELETE SET NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_artifacts_tool ON artifacts(tool);\n\n-- ============================================================\n-- ホスト\n-- ============================================================\nCREATE TABLE IF NOT EXISTS hosts (\n id TEXT PRIMARY KEY,\n authority_kind TEXT NOT NULL, -- \"IP\" | \"DOMAIN\"\n authority TEXT NOT NULL UNIQUE, -- IP アドレスまたはドメイン名\n resolved_ips_json TEXT NOT NULL DEFAULT '[]',\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n);\n\n-- ============================================================\n-- バーチャルホスト\n-- ============================================================\nCREATE TABLE IF NOT EXISTS vhosts (\n id TEXT PRIMARY KEY,\n host_id TEXT NOT NULL,\n hostname TEXT NOT NULL,\n source TEXT, -- \"nmap\" | \"cert\" | \"header\" | \"manual\"\n evidence_artifact_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n UNIQUE (host_id, hostname),\n FOREIGN KEY (host_id) REFERENCES hosts(id) ON DELETE CASCADE,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_vhosts_host ON vhosts(host_id);\n\n-- ============================================================\n-- サービス\n-- ============================================================\nCREATE TABLE IF NOT EXISTS services (\n id TEXT PRIMARY KEY,\n host_id TEXT NOT NULL,\n transport TEXT NOT NULL, -- \"tcp\" | \"udp\"\n port INTEGER NOT NULL,\n app_proto TEXT NOT NULL, -- \"http\" | \"ssh\" | \"ftp\" 等\n proto_confidence TEXT NOT NULL, -- \"high\" | \"medium\" | \"low\"\n banner TEXT,\n product TEXT,\n version TEXT,\n state TEXT NOT NULL, -- \"open\" | \"closed\" | \"filtered\"\n evidence_artifact_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE (host_id, transport, port),\n FOREIGN KEY (host_id) REFERENCES hosts(id) ON DELETE CASCADE,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_services_host ON services(host_id);\n\n-- ============================================================\n-- サービス観測(key-value)\n-- ============================================================\nCREATE TABLE IF NOT EXISTS service_observations (\n id TEXT PRIMARY KEY,\n service_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n confidence TEXT NOT NULL, -- \"high\" | \"medium\" | \"low\"\n evidence_artifact_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_svc_obs_service ON service_observations(service_id);\n\n-- ============================================================\n-- HTTP エンドポイント\n-- ============================================================\nCREATE TABLE IF NOT EXISTS http_endpoints (\n id TEXT PRIMARY KEY,\n service_id TEXT NOT NULL,\n vhost_id TEXT,\n base_uri TEXT NOT NULL, -- \"http://example.com:80\"\n method TEXT NOT NULL, -- \"GET\" | \"POST\" | ...\n path TEXT NOT NULL, -- \"/admin\"(クエリは含めない)\n status_code INTEGER,\n content_length INTEGER,\n words INTEGER,\n lines INTEGER,\n evidence_artifact_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n UNIQUE (service_id, method, path),\n FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE,\n FOREIGN KEY (vhost_id) REFERENCES vhosts(id) ON DELETE SET NULL,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_endpoints_service ON http_endpoints(service_id);\n\n-- ============================================================\n-- 入力パラメータ\n-- ============================================================\nCREATE TABLE IF NOT EXISTS inputs (\n id TEXT PRIMARY KEY,\n service_id TEXT NOT NULL,\n location TEXT NOT NULL, -- \"query\" | \"path\" | \"body\" | \"header\" | \"cookie\"\n name TEXT NOT NULL,\n type_hint TEXT, -- \"string\" | \"int\" | \"json\" 等(任意)\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE (service_id, location, name),\n FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_inputs_service ON inputs(service_id);\n\n-- ============================================================\n-- エンドポイント ↔ 入力(多対多)\n-- ============================================================\nCREATE TABLE IF NOT EXISTS endpoint_inputs (\n id TEXT PRIMARY KEY,\n endpoint_id TEXT NOT NULL,\n input_id TEXT NOT NULL,\n evidence_artifact_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n UNIQUE (endpoint_id, input_id),\n FOREIGN KEY (endpoint_id) REFERENCES http_endpoints(id) ON DELETE CASCADE,\n FOREIGN KEY (input_id) REFERENCES inputs(id) ON DELETE CASCADE,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_ep_inputs_endpoint ON endpoint_inputs(endpoint_id);\nCREATE INDEX IF NOT EXISTS idx_ep_inputs_input ON endpoint_inputs(input_id);\n\n-- ============================================================\n-- 観測値\n-- ============================================================\nCREATE TABLE IF NOT EXISTS observations (\n id TEXT PRIMARY KEY,\n input_id TEXT NOT NULL,\n raw_value TEXT NOT NULL,\n norm_value TEXT NOT NULL,\n body_path TEXT, -- JSON Pointer 等(例: \"/user/name\")\n source TEXT NOT NULL, -- \"ffuf_url\" | \"req_query\" | \"req_body\" | \"manual\"\n confidence TEXT NOT NULL, -- \"high\" | \"medium\" | \"low\"\n evidence_artifact_id TEXT NOT NULL,\n observed_at TEXT NOT NULL,\n FOREIGN KEY (input_id) REFERENCES inputs(id) ON DELETE CASCADE,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_obs_input ON observations(input_id);\n\n-- ============================================================\n-- 認証情報\n-- ============================================================\nCREATE TABLE IF NOT EXISTS credentials (\n id TEXT PRIMARY KEY,\n service_id TEXT NOT NULL,\n endpoint_id TEXT, -- HTTP の場合のみ(任意)\n username TEXT NOT NULL,\n secret TEXT NOT NULL,\n secret_type TEXT NOT NULL, -- \"password\" | \"token\" | \"api_key\" | \"ssh_key\"\n source TEXT NOT NULL, -- \"brute_force\" | \"default\" | \"leaked\" | \"manual\"\n confidence TEXT NOT NULL, -- \"high\" | \"medium\" | \"low\"\n evidence_artifact_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE,\n FOREIGN KEY (endpoint_id) REFERENCES http_endpoints(id) ON DELETE SET NULL,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_creds_service ON credentials(service_id);\nCREATE INDEX IF NOT EXISTS idx_creds_endpoint ON credentials(endpoint_id);\n\n-- ============================================================\n-- 脆弱性\n-- ============================================================\nCREATE TABLE IF NOT EXISTS vulnerabilities (\n id TEXT PRIMARY KEY,\n service_id TEXT NOT NULL,\n endpoint_id TEXT, -- HTTP の場合のみ(任意)\n vuln_type TEXT NOT NULL, -- \"sqli\" | \"xss\" | \"rce\" | \"lfi\" | \"ssrf\" | ...\n title TEXT NOT NULL,\n description TEXT,\n severity TEXT NOT NULL, -- \"critical\" | \"high\" | \"medium\" | \"low\" | \"info\"\n confidence TEXT NOT NULL, -- \"high\" | \"medium\" | \"low\"\n evidence_artifact_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE,\n FOREIGN KEY (endpoint_id) REFERENCES http_endpoints(id) ON DELETE SET NULL,\n FOREIGN KEY (evidence_artifact_id) REFERENCES artifacts(id) ON DELETE RESTRICT\n);\n\nCREATE INDEX IF NOT EXISTS idx_vulns_service ON vulnerabilities(service_id);\nCREATE INDEX IF NOT EXISTS idx_vulns_endpoint ON vulnerabilities(endpoint_id);\nCREATE INDEX IF NOT EXISTS idx_vulns_severity ON vulnerabilities(severity);\n\n-- ============================================================\n-- CVE 情報\n-- ============================================================\nCREATE TABLE IF NOT EXISTS cves (\n id TEXT PRIMARY KEY,\n vulnerability_id TEXT NOT NULL,\n cve_id TEXT NOT NULL, -- \"CVE-YYYY-NNNNN\"\n description TEXT,\n cvss_score REAL,\n cvss_vector TEXT,\n reference_url TEXT,\n created_at TEXT NOT NULL,\n FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_cves_vuln ON cves(vulnerability_id);\nCREATE INDEX IF NOT EXISTS idx_cves_cveid ON cves(cve_id);\n\n-- ============================================================\n-- Datalog ルール保存\n-- ============================================================\nCREATE TABLE IF NOT EXISTS datalog_rules (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n description TEXT,\n rule_text TEXT NOT NULL,\n generated_by TEXT NOT NULL, -- \"human\" | \"ai\" | \"preset\"\n is_preset INTEGER NOT NULL DEFAULT 0,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_datalog_rules_name ON datalog_rules(name);\n` as const;\n","import type Database from 'better-sqlite3';\nimport { SCHEMA_SQL } from './schema.js';\n\n/**\n * Run schema SQL to create all tables.\n * Enables foreign key enforcement before executing the schema.\n */\nexport function migrateDatabase(db: Database.Database): void {\n db.pragma('foreign_keys = ON');\n db.exec(SCHEMA_SQL);\n}\n","/**\r\n * sonobat — MCP Server\r\n *\r\n * Creates and configures the MCP server with all tools and resources.\r\n */\r\n\r\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\r\nimport type Database from 'better-sqlite3';\r\nimport { registerQueryTools } from './tools/query.js';\r\nimport { registerIngestTool } from './tools/ingest.js';\r\nimport { registerProposeTool } from './tools/propose.js';\r\nimport { registerMutationTools } from './tools/mutation.js';\r\nimport { registerDatalogTools } from './tools/datalog.js';\r\nimport { registerResources } from './resources.js';\r\n\r\n/**\r\n * Create a fully configured MCP server with all sonobat tools and resources.\r\n *\r\n * @param db - The better-sqlite3 database instance\r\n * @returns Configured McpServer instance\r\n */\r\nexport function createMcpServer(db: Database.Database): McpServer {\r\n const server = new McpServer({\r\n name: 'sonobat',\r\n version: '0.2.0',\r\n });\r\n\r\n // Register tools\r\n registerQueryTools(server, db);\r\n registerIngestTool(server, db);\r\n registerProposeTool(server, db);\r\n registerMutationTools(server, db);\r\n registerDatalogTools(server, db);\r\n\r\n // Register resources\r\n registerResources(server, db);\r\n\r\n return server;\r\n}\r\n","/**\n * sonobat — MCP Query Tools\n *\n * Read-only tools for querying the AttackDataGraph.\n */\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type Database from 'better-sqlite3';\nimport { z } from 'zod';\nimport { HostRepository } from '../../db/repository/host-repository.js';\nimport { ServiceRepository } from '../../db/repository/service-repository.js';\nimport { VhostRepository } from '../../db/repository/vhost-repository.js';\nimport { HttpEndpointRepository } from '../../db/repository/http-endpoint-repository.js';\nimport { InputRepository } from '../../db/repository/input-repository.js';\nimport { ObservationRepository } from '../../db/repository/observation-repository.js';\nimport { CredentialRepository } from '../../db/repository/credential-repository.js';\nimport { VulnerabilityRepository } from '../../db/repository/vulnerability-repository.js';\n\nexport function registerQueryTools(server: McpServer, db: Database.Database): void {\n const hostRepo = new HostRepository(db);\n const serviceRepo = new ServiceRepository(db);\n const vhostRepo = new VhostRepository(db);\n const httpEndpointRepo = new HttpEndpointRepository(db);\n const inputRepo = new InputRepository(db);\n const observationRepo = new ObservationRepository(db);\n const credentialRepo = new CredentialRepository(db);\n const vulnRepo = new VulnerabilityRepository(db);\n\n // 1. list_hosts\n server.tool('list_hosts', 'List all discovered hosts', {}, async () => {\n const hosts = hostRepo.findAll();\n return { content: [{ type: 'text', text: JSON.stringify(hosts, null, 2) }] };\n });\n\n // 2. get_host\n server.tool(\n 'get_host',\n 'Get detailed information about a host including services and vhosts',\n { hostId: z.string().describe('Host UUID') },\n async ({ hostId }) => {\n const host = hostRepo.findById(hostId);\n if (!host) {\n return { content: [{ type: 'text', text: `Host not found: ${hostId}` }], isError: true };\n }\n const services = serviceRepo.findByHostId(hostId);\n const vhosts = vhostRepo.findByHostId(hostId);\n const result = { ...host, services, vhosts };\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n },\n );\n\n // 3. list_services\n server.tool(\n 'list_services',\n 'List all services for a host',\n { hostId: z.string().describe('Host UUID') },\n async ({ hostId }) => {\n const services = serviceRepo.findByHostId(hostId);\n return { content: [{ type: 'text', text: JSON.stringify(services, null, 2) }] };\n },\n );\n\n // 4. list_endpoints\n server.tool(\n 'list_endpoints',\n 'List all HTTP endpoints for a service',\n { serviceId: z.string().describe('Service UUID') },\n async ({ serviceId }) => {\n const endpoints = httpEndpointRepo.findByServiceId(serviceId);\n return { content: [{ type: 'text', text: JSON.stringify(endpoints, null, 2) }] };\n },\n );\n\n // 5. list_inputs\n server.tool(\n 'list_inputs',\n 'List all input parameters for a service, optionally filtered by location',\n {\n serviceId: z.string().describe('Service UUID'),\n location: z.string().optional().describe('Filter by location (query, path, body, header, cookie)'),\n },\n async ({ serviceId, location }) => {\n const inputs = inputRepo.findByServiceId(serviceId, location);\n return { content: [{ type: 'text', text: JSON.stringify(inputs, null, 2) }] };\n },\n );\n\n // 6. list_observations\n server.tool(\n 'list_observations',\n 'List all observations for an input parameter',\n { inputId: z.string().describe('Input UUID') },\n async ({ inputId }) => {\n const observations = observationRepo.findByInputId(inputId);\n return { content: [{ type: 'text', text: JSON.stringify(observations, null, 2) }] };\n },\n );\n\n // 7. list_credentials\n server.tool(\n 'list_credentials',\n 'List credentials, optionally filtered by service',\n {\n serviceId: z.string().optional().describe('Service UUID (optional, omit to list all)'),\n },\n async ({ serviceId }) => {\n const credentials = serviceId\n ? credentialRepo.findByServiceId(serviceId)\n : credentialRepo.findAll();\n return { content: [{ type: 'text', text: JSON.stringify(credentials, null, 2) }] };\n },\n );\n\n // 8. list_vulnerabilities\n server.tool(\n 'list_vulnerabilities',\n 'List vulnerabilities, optionally filtered by service and/or severity',\n {\n serviceId: z.string().optional().describe('Service UUID (optional, omit to list all)'),\n severity: z.string().optional().describe('Filter by severity (critical, high, medium, low, info)'),\n },\n async ({ serviceId, severity }) => {\n const vulns = serviceId\n ? vulnRepo.findByServiceId(serviceId, severity)\n : vulnRepo.findAll(severity);\n return { content: [{ type: 'text', text: JSON.stringify(vulns, null, 2) }] };\n },\n );\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Host } from '../../types/entities.js';\nimport type { CreateHostInput, UpdateHostInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `hosts` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface HostRow {\n id: string;\n authority_kind: string;\n authority: string;\n resolved_ips_json: string;\n created_at: string;\n updated_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Host entity. */\nfunction rowToHost(row: HostRow): Host {\n return {\n id: row.id,\n authorityKind: row.authority_kind,\n authority: row.authority,\n resolvedIpsJson: row.resolved_ips_json,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\n/**\n * Repository for the `hosts` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class HostRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Host and return the full entity. */\n create(input: CreateHostInput): Host {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string, string, string, string]\n >(\n `INSERT INTO hosts (id, authority_kind, authority, resolved_ips_json, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.authorityKind,\n input.authority,\n input.resolvedIpsJson,\n now,\n now,\n );\n\n return {\n id,\n authorityKind: input.authorityKind,\n authority: input.authority,\n resolvedIpsJson: input.resolvedIpsJson,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n /** Find a Host by its primary key. Returns undefined if not found. */\n findById(id: string): Host | undefined {\n const stmt = this.db.prepare<[string], HostRow>(\n `SELECT id, authority_kind, authority, resolved_ips_json, created_at, updated_at\n FROM hosts\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToHost(row) : undefined;\n }\n\n /** Return all Hosts. */\n findAll(): Host[] {\n const stmt = this.db.prepare<[], HostRow>(\n `SELECT id, authority_kind, authority, resolved_ips_json, created_at, updated_at\n FROM hosts`,\n );\n\n const rows = stmt.all();\n return rows.map(rowToHost);\n }\n\n /** Find a Host by its unique authority value. Returns undefined if not found. */\n findByAuthority(authority: string): Host | undefined {\n const stmt = this.db.prepare<[string], HostRow>(\n `SELECT id, authority_kind, authority, resolved_ips_json, created_at, updated_at\n FROM hosts\n WHERE authority = ?`,\n );\n\n const row = stmt.get(authority);\n return row ? rowToHost(row) : undefined;\n }\n\n /**\n * Update an existing Host with the provided fields.\n * Always bumps `updated_at`. Returns the updated entity, or undefined if\n * the Host was not found.\n */\n update(id: string, input: UpdateHostInput): Host | undefined {\n const setClauses: string[] = [];\n const params: unknown[] = [];\n\n if (input.resolvedIpsJson !== undefined) {\n setClauses.push('resolved_ips_json = ?');\n params.push(input.resolvedIpsJson);\n }\n\n const now = new Date().toISOString();\n setClauses.push('updated_at = ?');\n params.push(now);\n\n // Always include the WHERE id\n params.push(id);\n\n const sql = `UPDATE hosts SET ${setClauses.join(', ')} WHERE id = ?`;\n const stmt = this.db.prepare(sql);\n const result = stmt.run(...params);\n\n if (result.changes === 0) {\n return undefined;\n }\n\n return this.findById(id);\n }\n\n /** Delete a Host by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM hosts WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Service } from '../../types/entities.js';\nimport type { CreateServiceInput, UpdateServiceInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `services` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface ServiceRow {\n id: string;\n host_id: string;\n transport: string;\n port: number;\n app_proto: string;\n proto_confidence: string;\n banner: string | null;\n product: string | null;\n version: string | null;\n state: string;\n evidence_artifact_id: string;\n created_at: string;\n updated_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Service entity. */\nfunction rowToService(row: ServiceRow): Service {\n return {\n id: row.id,\n hostId: row.host_id,\n transport: row.transport,\n port: row.port,\n appProto: row.app_proto,\n protoConfidence: row.proto_confidence,\n banner: row.banner ?? undefined,\n product: row.product ?? undefined,\n version: row.version ?? undefined,\n state: row.state,\n evidenceArtifactId: row.evidence_artifact_id,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\n/**\n * Repository for the `services` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class ServiceRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Service and return the full entity. */\n create(input: CreateServiceInput): Service {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string, number, string, string, string | null, string | null, string | null, string, string, string, string]\n >(\n `INSERT INTO services (id, host_id, transport, port, app_proto, proto_confidence, banner, product, version, state, evidence_artifact_id, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.hostId,\n input.transport,\n input.port,\n input.appProto,\n input.protoConfidence,\n input.banner ?? null,\n input.product ?? null,\n input.version ?? null,\n input.state,\n input.evidenceArtifactId,\n now,\n now,\n );\n\n return {\n id,\n hostId: input.hostId,\n transport: input.transport,\n port: input.port,\n appProto: input.appProto,\n protoConfidence: input.protoConfidence,\n banner: input.banner,\n product: input.product,\n version: input.version,\n state: input.state,\n evidenceArtifactId: input.evidenceArtifactId,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n /** Find a Service by its primary key. Returns undefined if not found. */\n findById(id: string): Service | undefined {\n const stmt = this.db.prepare<[string], ServiceRow>(\n `SELECT id, host_id, transport, port, app_proto, proto_confidence, banner, product, version, state, evidence_artifact_id, created_at, updated_at\n FROM services\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToService(row) : undefined;\n }\n\n /** Find all Services belonging to a given host. */\n findByHostId(hostId: string): Service[] {\n const stmt = this.db.prepare<[string], ServiceRow>(\n `SELECT id, host_id, transport, port, app_proto, proto_confidence, banner, product, version, state, evidence_artifact_id, created_at, updated_at\n FROM services\n WHERE host_id = ?`,\n );\n\n const rows = stmt.all(hostId);\n return rows.map(rowToService);\n }\n\n /**\n * Update an existing Service with the provided fields.\n * Always bumps `updated_at`. Returns the updated entity, or undefined if\n * the Service was not found.\n */\n update(id: string, input: UpdateServiceInput): Service | undefined {\n const setClauses: string[] = [];\n const params: unknown[] = [];\n\n if (input.appProto !== undefined) {\n setClauses.push('app_proto = ?');\n params.push(input.appProto);\n }\n\n if (input.protoConfidence !== undefined) {\n setClauses.push('proto_confidence = ?');\n params.push(input.protoConfidence);\n }\n\n if (input.banner !== undefined) {\n setClauses.push('banner = ?');\n params.push(input.banner);\n }\n\n if (input.product !== undefined) {\n setClauses.push('product = ?');\n params.push(input.product);\n }\n\n if (input.version !== undefined) {\n setClauses.push('version = ?');\n params.push(input.version);\n }\n\n if (input.state !== undefined) {\n setClauses.push('state = ?');\n params.push(input.state);\n }\n\n const now = new Date().toISOString();\n setClauses.push('updated_at = ?');\n params.push(now);\n\n // Always include the WHERE id\n params.push(id);\n\n const sql = `UPDATE services SET ${setClauses.join(', ')} WHERE id = ?`;\n const stmt = this.db.prepare(sql);\n const result = stmt.run(...params);\n\n if (result.changes === 0) {\n return undefined;\n }\n\n return this.findById(id);\n }\n\n /** Delete a Service by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM services WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Vhost } from '../../types/entities.js';\nimport type { CreateVhostInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `vhosts` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface VhostRow {\n id: string;\n host_id: string;\n hostname: string;\n source: string | null;\n evidence_artifact_id: string;\n created_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Vhost entity. */\nfunction rowToVhost(row: VhostRow): Vhost {\n return {\n id: row.id,\n hostId: row.host_id,\n hostname: row.hostname,\n source: row.source ?? undefined,\n evidenceArtifactId: row.evidence_artifact_id,\n createdAt: row.created_at,\n };\n}\n\n/**\n * Repository for the `vhosts` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class VhostRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Vhost and return the full entity. */\n create(input: CreateVhostInput): Vhost {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string, string | null, string, string]\n >(\n `INSERT INTO vhosts (id, host_id, hostname, source, evidence_artifact_id, created_at)\n VALUES (?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.hostId,\n input.hostname,\n input.source ?? null,\n input.evidenceArtifactId,\n now,\n );\n\n return {\n id,\n hostId: input.hostId,\n hostname: input.hostname,\n source: input.source,\n evidenceArtifactId: input.evidenceArtifactId,\n createdAt: now,\n };\n }\n\n /** Find a Vhost by its primary key. Returns undefined if not found. */\n findById(id: string): Vhost | undefined {\n const stmt = this.db.prepare<[string], VhostRow>(\n `SELECT id, host_id, hostname, source, evidence_artifact_id, created_at\n FROM vhosts\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToVhost(row) : undefined;\n }\n\n /** Return all Vhosts belonging to a given host. */\n findByHostId(hostId: string): Vhost[] {\n const stmt = this.db.prepare<[string], VhostRow>(\n `SELECT id, host_id, hostname, source, evidence_artifact_id, created_at\n FROM vhosts\n WHERE host_id = ?`,\n );\n\n const rows = stmt.all(hostId);\n return rows.map(rowToVhost);\n }\n\n /** Delete a Vhost by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM vhosts WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { HttpEndpoint } from '../../types/entities.js';\nimport type { CreateHttpEndpointInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `http_endpoints` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface HttpEndpointRow {\n id: string;\n service_id: string;\n vhost_id: string | null;\n base_uri: string;\n method: string;\n path: string;\n status_code: number | null;\n content_length: number | null;\n words: number | null;\n lines: number | null;\n evidence_artifact_id: string;\n created_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase HttpEndpoint entity. */\nfunction rowToHttpEndpoint(row: HttpEndpointRow): HttpEndpoint {\n return {\n id: row.id,\n serviceId: row.service_id,\n vhostId: row.vhost_id ?? undefined,\n baseUri: row.base_uri,\n method: row.method,\n path: row.path,\n statusCode: row.status_code ?? undefined,\n contentLength: row.content_length ?? undefined,\n words: row.words ?? undefined,\n lines: row.lines ?? undefined,\n evidenceArtifactId: row.evidence_artifact_id,\n createdAt: row.created_at,\n };\n}\n\n/**\n * Repository for the `http_endpoints` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class HttpEndpointRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new HttpEndpoint and return the full entity. */\n create(input: CreateHttpEndpointInput): HttpEndpoint {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare(\n `INSERT INTO http_endpoints (id, service_id, vhost_id, base_uri, method, path, status_code, content_length, words, lines, evidence_artifact_id, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.serviceId,\n input.vhostId ?? null,\n input.baseUri,\n input.method,\n input.path,\n input.statusCode ?? null,\n input.contentLength ?? null,\n input.words ?? null,\n input.lines ?? null,\n input.evidenceArtifactId,\n now,\n );\n\n return {\n id,\n serviceId: input.serviceId,\n vhostId: input.vhostId,\n baseUri: input.baseUri,\n method: input.method,\n path: input.path,\n statusCode: input.statusCode,\n contentLength: input.contentLength,\n words: input.words,\n lines: input.lines,\n evidenceArtifactId: input.evidenceArtifactId,\n createdAt: now,\n };\n }\n\n /** Find an HttpEndpoint by its primary key. Returns undefined if not found. */\n findById(id: string): HttpEndpoint | undefined {\n const stmt = this.db.prepare<[string], HttpEndpointRow>(\n `SELECT id, service_id, vhost_id, base_uri, method, path, status_code, content_length, words, lines, evidence_artifact_id, created_at\n FROM http_endpoints\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToHttpEndpoint(row) : undefined;\n }\n\n /** Return all HttpEndpoints for a given service. */\n findByServiceId(serviceId: string): HttpEndpoint[] {\n const stmt = this.db.prepare<[string], HttpEndpointRow>(\n `SELECT id, service_id, vhost_id, base_uri, method, path, status_code, content_length, words, lines, evidence_artifact_id, created_at\n FROM http_endpoints\n WHERE service_id = ?`,\n );\n\n const rows = stmt.all(serviceId);\n return rows.map(rowToHttpEndpoint);\n }\n\n /** Delete an HttpEndpoint by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>(\n 'DELETE FROM http_endpoints WHERE id = ?',\n );\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Input } from '../../types/entities.js';\nimport type { CreateInputInput, UpdateInputInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `inputs` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface InputRow {\n id: string;\n service_id: string;\n location: string;\n name: string;\n type_hint: string | null;\n created_at: string;\n updated_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Input entity. */\nfunction rowToInput(row: InputRow): Input {\n return {\n id: row.id,\n serviceId: row.service_id,\n location: row.location,\n name: row.name,\n typeHint: row.type_hint ?? undefined,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\n/**\n * Repository for the `inputs` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class InputRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Input and return the full entity. */\n create(input: CreateInputInput): Input {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string, string, string | null, string, string]\n >(\n `INSERT INTO inputs (id, service_id, location, name, type_hint, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.serviceId,\n input.location,\n input.name,\n input.typeHint ?? null,\n now,\n now,\n );\n\n return {\n id,\n serviceId: input.serviceId,\n location: input.location,\n name: input.name,\n typeHint: input.typeHint,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n /** Find an Input by its primary key. Returns undefined if not found. */\n findById(id: string): Input | undefined {\n const stmt = this.db.prepare<[string], InputRow>(\n `SELECT id, service_id, location, name, type_hint, created_at, updated_at\n FROM inputs\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToInput(row) : undefined;\n }\n\n /**\n * Find all Inputs for a given service.\n * If location is provided, further filter by location.\n */\n findByServiceId(serviceId: string, location?: string): Input[] {\n if (location !== undefined) {\n const stmt = this.db.prepare<[string, string], InputRow>(\n `SELECT id, service_id, location, name, type_hint, created_at, updated_at\n FROM inputs\n WHERE service_id = ? AND location = ?`,\n );\n\n const rows = stmt.all(serviceId, location);\n return rows.map(rowToInput);\n }\n\n const stmt = this.db.prepare<[string], InputRow>(\n `SELECT id, service_id, location, name, type_hint, created_at, updated_at\n FROM inputs\n WHERE service_id = ?`,\n );\n\n const rows = stmt.all(serviceId);\n return rows.map(rowToInput);\n }\n\n /**\n * Update an existing Input with the provided fields.\n * Always bumps `updated_at`. Returns the updated entity, or undefined if\n * the Input was not found.\n */\n update(id: string, input: UpdateInputInput): Input | undefined {\n const setClauses: string[] = [];\n const params: unknown[] = [];\n\n if (input.typeHint !== undefined) {\n setClauses.push('type_hint = ?');\n params.push(input.typeHint);\n }\n\n const now = new Date().toISOString();\n setClauses.push('updated_at = ?');\n params.push(now);\n\n // Always include the WHERE id\n params.push(id);\n\n const sql = `UPDATE inputs SET ${setClauses.join(', ')} WHERE id = ?`;\n const stmt = this.db.prepare(sql);\n const result = stmt.run(...params);\n\n if (result.changes === 0) {\n return undefined;\n }\n\n return this.findById(id);\n }\n\n /** Delete an Input by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM inputs WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Observation } from '../../types/entities.js';\nimport type { CreateObservationInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `observations` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface ObservationRow {\n id: string;\n input_id: string;\n raw_value: string;\n norm_value: string;\n body_path: string | null;\n source: string;\n confidence: string;\n evidence_artifact_id: string;\n observed_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Observation entity. */\nfunction rowToObservation(row: ObservationRow): Observation {\n return {\n id: row.id,\n inputId: row.input_id,\n rawValue: row.raw_value,\n normValue: row.norm_value,\n bodyPath: row.body_path ?? undefined,\n source: row.source,\n confidence: row.confidence,\n evidenceArtifactId: row.evidence_artifact_id,\n observedAt: row.observed_at,\n };\n}\n\n/**\n * Repository for the `observations` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class ObservationRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Observation and return the full entity. */\n create(input: CreateObservationInput): Observation {\n const id = crypto.randomUUID();\n\n const stmt = this.db.prepare<\n [string, string, string, string, string | null, string, string, string, string]\n >(\n `INSERT INTO observations (id, input_id, raw_value, norm_value, body_path, source, confidence, evidence_artifact_id, observed_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.inputId,\n input.rawValue,\n input.normValue,\n input.bodyPath ?? null,\n input.source,\n input.confidence,\n input.evidenceArtifactId,\n input.observedAt,\n );\n\n return {\n id,\n inputId: input.inputId,\n rawValue: input.rawValue,\n normValue: input.normValue,\n bodyPath: input.bodyPath,\n source: input.source,\n confidence: input.confidence,\n evidenceArtifactId: input.evidenceArtifactId,\n observedAt: input.observedAt,\n };\n }\n\n /** Find an Observation by its primary key. Returns undefined if not found. */\n findById(id: string): Observation | undefined {\n const stmt = this.db.prepare<[string], ObservationRow>(\n `SELECT id, input_id, raw_value, norm_value, body_path, source, confidence, evidence_artifact_id, observed_at\n FROM observations\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToObservation(row) : undefined;\n }\n\n /** Return all Observations for a given input ID. */\n findByInputId(inputId: string): Observation[] {\n const stmt = this.db.prepare<[string], ObservationRow>(\n `SELECT id, input_id, raw_value, norm_value, body_path, source, confidence, evidence_artifact_id, observed_at\n FROM observations\n WHERE input_id = ?`,\n );\n\n const rows = stmt.all(inputId);\n return rows.map(rowToObservation);\n }\n\n /** Delete an Observation by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM observations WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Credential } from '../../types/entities.js';\nimport type { CreateCredentialInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `credentials` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface CredentialRow {\n id: string;\n service_id: string;\n endpoint_id: string | null;\n username: string;\n secret: string;\n secret_type: string;\n source: string;\n confidence: string;\n evidence_artifact_id: string;\n created_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Credential entity. */\nfunction rowToCredential(row: CredentialRow): Credential {\n return {\n id: row.id,\n serviceId: row.service_id,\n endpointId: row.endpoint_id ?? undefined,\n username: row.username,\n secret: row.secret,\n secretType: row.secret_type,\n source: row.source,\n confidence: row.confidence,\n evidenceArtifactId: row.evidence_artifact_id,\n createdAt: row.created_at,\n };\n}\n\n/**\n * Repository for the `credentials` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class CredentialRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Credential and return the full entity. */\n create(input: CreateCredentialInput): Credential {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string | null, string, string, string, string, string, string, string]\n >(\n `INSERT INTO credentials (id, service_id, endpoint_id, username, secret, secret_type, source, confidence, evidence_artifact_id, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.serviceId,\n input.endpointId ?? null,\n input.username,\n input.secret,\n input.secretType,\n input.source,\n input.confidence,\n input.evidenceArtifactId,\n now,\n );\n\n return {\n id,\n serviceId: input.serviceId,\n endpointId: input.endpointId ?? undefined,\n username: input.username,\n secret: input.secret,\n secretType: input.secretType,\n source: input.source,\n confidence: input.confidence,\n evidenceArtifactId: input.evidenceArtifactId,\n createdAt: now,\n };\n }\n\n /** Find a Credential by its primary key. Returns undefined if not found. */\n findById(id: string): Credential | undefined {\n const stmt = this.db.prepare<[string], CredentialRow>(\n `SELECT id, service_id, endpoint_id, username, secret, secret_type, source, confidence, evidence_artifact_id, created_at\n FROM credentials\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToCredential(row) : undefined;\n }\n\n /** Return all Credentials for a given service. */\n findByServiceId(serviceId: string): Credential[] {\n const stmt = this.db.prepare<[string], CredentialRow>(\n `SELECT id, service_id, endpoint_id, username, secret, secret_type, source, confidence, evidence_artifact_id, created_at\n FROM credentials\n WHERE service_id = ?`,\n );\n\n const rows = stmt.all(serviceId);\n return rows.map(rowToCredential);\n }\n\n /** Return all Credentials across all services. */\n findAll(): Credential[] {\n const stmt = this.db.prepare<[], CredentialRow>(\n `SELECT id, service_id, endpoint_id, username, secret, secret_type, source, confidence, evidence_artifact_id, created_at\n FROM credentials`,\n );\n\n const rows = stmt.all();\n return rows.map(rowToCredential);\n }\n\n /** Delete a Credential by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM credentials WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Vulnerability } from '../../types/entities.js';\nimport type { CreateVulnerabilityInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `vulnerabilities` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface VulnerabilityRow {\n id: string;\n service_id: string;\n endpoint_id: string | null;\n vuln_type: string;\n title: string;\n description: string | null;\n severity: string;\n confidence: string;\n evidence_artifact_id: string;\n created_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Vulnerability entity. */\nfunction rowToVulnerability(row: VulnerabilityRow): Vulnerability {\n return {\n id: row.id,\n serviceId: row.service_id,\n endpointId: row.endpoint_id ?? undefined,\n vulnType: row.vuln_type,\n title: row.title,\n description: row.description ?? undefined,\n severity: row.severity,\n confidence: row.confidence,\n evidenceArtifactId: row.evidence_artifact_id,\n createdAt: row.created_at,\n };\n}\n\n/**\n * Repository for the `vulnerabilities` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class VulnerabilityRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Vulnerability and return the full entity. */\n create(input: CreateVulnerabilityInput): Vulnerability {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare(\n `INSERT INTO vulnerabilities (id, service_id, endpoint_id, vuln_type, title, description, severity, confidence, evidence_artifact_id, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.serviceId,\n input.endpointId ?? null,\n input.vulnType,\n input.title,\n input.description ?? null,\n input.severity,\n input.confidence,\n input.evidenceArtifactId,\n now,\n );\n\n return {\n id,\n serviceId: input.serviceId,\n endpointId: input.endpointId,\n vulnType: input.vulnType,\n title: input.title,\n description: input.description,\n severity: input.severity,\n confidence: input.confidence,\n evidenceArtifactId: input.evidenceArtifactId,\n createdAt: now,\n };\n }\n\n /** Find a Vulnerability by its primary key. Returns undefined if not found. */\n findById(id: string): Vulnerability | undefined {\n const stmt = this.db.prepare<[string], VulnerabilityRow>(\n `SELECT id, service_id, endpoint_id, vuln_type, title, description, severity, confidence, evidence_artifact_id, created_at\n FROM vulnerabilities\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToVulnerability(row) : undefined;\n }\n\n /**\n * Return all Vulnerabilities for a given service.\n * Optionally filter by severity when the parameter is provided.\n */\n findByServiceId(serviceId: string, severity?: string): Vulnerability[] {\n if (severity !== undefined) {\n const stmt = this.db.prepare<[string, string], VulnerabilityRow>(\n `SELECT id, service_id, endpoint_id, vuln_type, title, description, severity, confidence, evidence_artifact_id, created_at\n FROM vulnerabilities\n WHERE service_id = ? AND severity = ?`,\n );\n\n const rows = stmt.all(serviceId, severity);\n return rows.map(rowToVulnerability);\n }\n\n const stmt = this.db.prepare<[string], VulnerabilityRow>(\n `SELECT id, service_id, endpoint_id, vuln_type, title, description, severity, confidence, evidence_artifact_id, created_at\n FROM vulnerabilities\n WHERE service_id = ?`,\n );\n\n const rows = stmt.all(serviceId);\n return rows.map(rowToVulnerability);\n }\n\n /**\n * Return all Vulnerabilities across all services.\n * Optionally filter by severity when the parameter is provided.\n */\n findAll(severity?: string): Vulnerability[] {\n if (severity !== undefined) {\n const stmt = this.db.prepare<[string], VulnerabilityRow>(\n `SELECT id, service_id, endpoint_id, vuln_type, title, description, severity, confidence, evidence_artifact_id, created_at\n FROM vulnerabilities\n WHERE severity = ?`,\n );\n\n const rows = stmt.all(severity);\n return rows.map(rowToVulnerability);\n }\n\n const stmt = this.db.prepare<[], VulnerabilityRow>(\n `SELECT id, service_id, endpoint_id, vuln_type, title, description, severity, confidence, evidence_artifact_id, created_at\n FROM vulnerabilities`,\n );\n\n const rows = stmt.all();\n return rows.map(rowToVulnerability);\n }\n\n /** Delete a Vulnerability by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM vulnerabilities WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","/**\n * sonobat — MCP Ingest Tool\n *\n * Tool for ingesting tool output files into the AttackDataGraph.\n */\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type Database from 'better-sqlite3';\nimport { z } from 'zod';\nimport { ingest } from '../../engine/ingest.js';\n\nexport function registerIngestTool(server: McpServer, db: Database.Database): void {\n server.tool(\n 'ingest_file',\n 'Ingest a tool output file (nmap XML, ffuf JSON, nuclei JSONL) into the AttackDataGraph',\n {\n path: z.string().describe('Absolute path to the tool output file'),\n tool: z.enum(['nmap', 'ffuf', 'nuclei']).describe('Tool that produced the output'),\n },\n async ({ path, tool }) => {\n try {\n const result = ingest(db, { path, tool });\n const nr = result.normalizeResult;\n const summary = [\n `Ingested ${tool} output from ${path}`,\n `Artifact ID: ${result.artifactId}`,\n `Created: ${nr.hostsCreated} hosts, ${nr.servicesCreated} services, ${nr.httpEndpointsCreated} endpoints, ${nr.inputsCreated} inputs, ${nr.observationsCreated} observations, ${nr.vulnerabilitiesCreated} vulnerabilities, ${nr.cvesCreated} CVEs`,\n ].join('\\n');\n return { content: [{ type: 'text', text: summary }] };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { content: [{ type: 'text', text: `Ingest failed: ${message}` }], isError: true };\n }\n },\n );\n}\n","/**\n * sonobat — Ingest Engine\n *\n * ツール出力ファイルを読み込み、パース・正規化してDBに格納する。\n * ingestContent() はコアロジック(ファイルシステム非依存・テスト可能)。\n * ingest() はファイル読み込みの薄いラッパー。\n */\n\nimport type Database from 'better-sqlite3';\nimport fs from 'node:fs';\nimport crypto from 'node:crypto';\nimport path from 'node:path';\nimport type { IngestInput, IngestResult } from '../types/engine.js';\nimport { ArtifactRepository } from '../db/repository/artifact-repository.js';\nimport { parseNmapXml } from '../parser/nmap-parser.js';\nimport { parseFfufJson } from '../parser/ffuf-parser.js';\nimport { parseNucleiJsonl } from '../parser/nuclei-parser.js';\nimport { normalize } from './normalizer.js';\n\n/**\n * ツール出力の文字列を直接受け取り、パース・正規化してDBに格納する。\n * ファイルシステムに依存しないため、テストから直接呼び出せる。\n *\n * @param db better-sqlite3 の Database インスタンス\n * @param tool ツール種別('nmap' | 'ffuf' | 'nuclei')\n * @param content ツール出力の文字列\n * @param filePath Artifact に記録するファイルパス\n * @returns IngestResult(artifactId + normalizeResult)\n */\nexport function ingestContent(\n db: Database.Database,\n tool: 'nmap' | 'ffuf' | 'nuclei',\n content: string,\n filePath: string,\n): IngestResult {\n // 1. SHA-256 ハッシュを計算\n const sha256 = crypto.createHash('sha256').update(content).digest('hex');\n\n // 2. Artifact を作成\n const artifactRepo = new ArtifactRepository(db);\n const artifact = artifactRepo.create({\n tool,\n kind: 'tool_output',\n path: filePath,\n sha256,\n capturedAt: new Date().toISOString(),\n });\n\n // 3. ツール種別に応じてパース\n let parseResult;\n switch (tool) {\n case 'nmap':\n parseResult = parseNmapXml(content);\n break;\n case 'ffuf':\n parseResult = parseFfufJson(content);\n break;\n case 'nuclei':\n parseResult = parseNucleiJsonl(content);\n break;\n default: {\n // never 型による網羅性チェック — 未知の tool が渡された場合はコンパイルエラー\n const _exhaustive: never = tool;\n throw new Error(`Unknown tool: ${String(_exhaustive)}`);\n }\n }\n\n // 4. 正規化してDBに書き込む\n const normalizeResult = normalize(db, artifact.id, parseResult);\n\n // 5. 結果を返す\n return {\n artifactId: artifact.id,\n normalizeResult,\n };\n}\n\n/**\n * ファイルパスからツール出力を読み込み、インジェストする。\n * ingestContent() の薄いラッパー。\n *\n * @param db better-sqlite3 の Database インスタンス\n * @param input IngestInput(path + tool)\n * @returns IngestResult\n */\nexport function ingest(db: Database.Database, input: IngestInput): IngestResult {\n const resolved = path.resolve(input.path);\n const content = fs.readFileSync(resolved, 'utf-8');\n return ingestContent(db, input.tool, content, resolved);\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Artifact } from '../../types/entities.js';\nimport type { CreateArtifactInput } from '../../types/repository.js';\n\n/** Row shape returned by better-sqlite3 for the artifacts table. */\ninterface ArtifactRow {\n id: string;\n scan_id: string | null;\n tool: string;\n kind: string;\n path: string;\n sha256: string | null;\n captured_at: string;\n attrs_json: string | null;\n}\n\n/** Maps a snake_case DB row to a camelCase Artifact entity. */\nfunction rowToArtifact(row: ArtifactRow): Artifact {\n return {\n id: row.id,\n ...(row.scan_id !== null ? { scanId: row.scan_id } : {}),\n tool: row.tool,\n kind: row.kind,\n path: row.path,\n ...(row.sha256 !== null ? { sha256: row.sha256 } : {}),\n capturedAt: row.captured_at,\n ...(row.attrs_json !== null ? { attrsJson: row.attrs_json } : {}),\n };\n}\n\n/**\n * Repository for the `artifacts` table.\n *\n * Provides CRUD operations with camelCase ↔ snake_case mapping\n * between the TypeScript entity layer and the SQLite storage layer.\n */\nexport class ArtifactRepository {\n private readonly db: Database.Database;\n\n private readonly insertStmt: Database.Statement;\n private readonly selectByIdStmt: Database.Statement;\n private readonly selectAllStmt: Database.Statement;\n private readonly selectByToolStmt: Database.Statement;\n\n constructor(db: Database.Database) {\n this.db = db;\n\n this.insertStmt = this.db.prepare(\n 'INSERT INTO artifacts (id, scan_id, tool, kind, path, sha256, captured_at, attrs_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',\n );\n\n this.selectByIdStmt = this.db.prepare(\n 'SELECT id, scan_id, tool, kind, path, sha256, captured_at, attrs_json FROM artifacts WHERE id = ?',\n );\n\n this.selectAllStmt = this.db.prepare(\n 'SELECT id, scan_id, tool, kind, path, sha256, captured_at, attrs_json FROM artifacts',\n );\n\n this.selectByToolStmt = this.db.prepare(\n 'SELECT id, scan_id, tool, kind, path, sha256, captured_at, attrs_json FROM artifacts WHERE tool = ?',\n );\n }\n\n /** Create a new Artifact record and return the full entity. */\n create(input: CreateArtifactInput): Artifact {\n const id = crypto.randomUUID();\n\n this.insertStmt.run(\n id,\n input.scanId ?? null,\n input.tool,\n input.kind,\n input.path,\n input.sha256 ?? null,\n input.capturedAt,\n input.attrsJson ?? null,\n );\n\n return {\n id,\n ...(input.scanId !== undefined ? { scanId: input.scanId } : {}),\n tool: input.tool,\n kind: input.kind,\n path: input.path,\n ...(input.sha256 !== undefined ? { sha256: input.sha256 } : {}),\n capturedAt: input.capturedAt,\n ...(input.attrsJson !== undefined ? { attrsJson: input.attrsJson } : {}),\n };\n }\n\n /** Find an Artifact by its UUID. Returns undefined if not found. */\n findById(id: string): Artifact | undefined {\n const row = this.selectByIdStmt.get(id) as ArtifactRow | undefined;\n if (row === undefined) {\n return undefined;\n }\n return rowToArtifact(row);\n }\n\n /** Return all Artifact records. */\n findAll(): Artifact[] {\n const rows = this.selectAllStmt.all() as ArtifactRow[];\n return rows.map(rowToArtifact);\n }\n\n /** Return all Artifact records for the given tool name. */\n findByTool(tool: string): Artifact[] {\n const rows = this.selectByToolStmt.all(tool) as ArtifactRow[];\n return rows.map(rowToArtifact);\n }\n}\n","/**\n * sonobat — Nmap XML パーサー\n *\n * nmap の XML 出力を解析し、ParseResult 中間表現を返す。\n * fast-xml-parser を使用して XML をパースする。\n */\n\nimport { XMLParser } from 'fast-xml-parser';\nimport type {\n ParseResult,\n ParsedHost,\n ParsedService,\n ParsedServiceObservation,\n} from '../types/parser.js';\nimport { emptyParseResult } from '../types/parser.js';\n\n// ============================================================\n// XML パース後の型定義(unknown から安全に取り出すための構造)\n// ============================================================\n\ninterface NmapAddress {\n '@_addr': string;\n '@_addrtype': string;\n}\n\ninterface NmapHostname {\n '@_name': string;\n '@_type': string;\n}\n\ninterface NmapPortState {\n '@_state': string;\n '@_reason'?: string;\n}\n\ninterface NmapServiceAttr {\n '@_name'?: string;\n '@_product'?: string;\n '@_version'?: string;\n '@_extrainfo'?: string;\n '@_tunnel'?: string;\n '@_conf'?: string;\n}\n\ninterface NmapPort {\n '@_protocol': string;\n '@_portid': string;\n state: NmapPortState;\n service?: NmapServiceAttr;\n}\n\ninterface NmapOsMatch {\n '@_name': string;\n '@_accuracy': string;\n}\n\ninterface NmapHost {\n address: NmapAddress | NmapAddress[];\n hostnames?: {\n hostname?: NmapHostname | NmapHostname[];\n };\n ports?: {\n port?: NmapPort | NmapPort[];\n };\n os?: {\n osmatch?: NmapOsMatch | NmapOsMatch[];\n };\n}\n\ninterface NmapRun {\n nmaprun: {\n host?: NmapHost | NmapHost[];\n };\n}\n\n// ============================================================\n// ユーティリティ\n// ============================================================\n\n/** 値を配列に正規化する。undefined/null は空配列を返す。 */\nfunction ensureArray<T>(value: T | T[] | undefined | null): T[] {\n if (value === undefined || value === null) {\n return [];\n }\n return Array.isArray(value) ? value : [value];\n}\n\n/** nmap の conf 属性から protoConfidence を決定する */\nfunction toProtoConfidence(conf: string | undefined): string {\n const n = conf !== undefined ? Number(conf) : 0;\n if (n === 10) return 'high';\n if (n >= 7) return 'medium';\n return 'low';\n}\n\n/** OS accuracy から confidence を決定する */\nfunction toOsConfidence(accuracy: string): string {\n const n = Number(accuracy);\n if (n >= 90) return 'high';\n if (n >= 50) return 'medium';\n return 'low';\n}\n\n/** サービス名が HTTPS を示すかどうかを判定する */\nfunction isHttps(service: NmapServiceAttr): boolean {\n return service['@_name'] === 'https' || service['@_tunnel'] === 'ssl';\n}\n\n/** product, version, extrainfo からバナー文字列を合成する */\nfunction buildBanner(service: NmapServiceAttr): string | undefined {\n const parts: string[] = [];\n if (service['@_product']) parts.push(service['@_product']);\n if (service['@_version']) parts.push(service['@_version']);\n if (service['@_extrainfo']) parts.push(service['@_extrainfo']);\n return parts.length > 0 ? parts.join(' ') : undefined;\n}\n\n/** IPv4 アドレスを address 配列から取得する */\nfunction getIpv4Address(addresses: NmapAddress[]): string | undefined {\n const ipv4 = addresses.find((a) => a['@_addrtype'] === 'ipv4');\n return ipv4?.['@_addr'];\n}\n\n// ============================================================\n// メインパーサー\n// ============================================================\n\n/**\n * nmap XML 出力をパースし、ParseResult を返す。\n *\n * @param xml - nmap の XML 出力文字列\n * @returns ParseResult 中間表現\n */\nexport function parseNmapXml(xml: string): ParseResult {\n const parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n allowBooleanAttributes: true,\n });\n\n const parsed: unknown = parser.parse(xml);\n const nmapRun = parsed as NmapRun;\n\n const result = emptyParseResult();\n\n const hosts = ensureArray(nmapRun.nmaprun?.host);\n\n for (const host of hosts) {\n processHost(host, result);\n }\n\n return result;\n}\n\n/**\n * 単一の host 要素を処理し、結果に追加する。\n */\nfunction processHost(host: NmapHost, result: ParseResult): void {\n // --- ホスト情報 ---\n const addresses = ensureArray(host.address);\n const authority = getIpv4Address(addresses);\n if (authority === undefined) {\n return; // IPv4 アドレスがないホストはスキップ\n }\n\n const parsedHost: ParsedHost = {\n authority,\n authorityKind: 'IP',\n };\n result.hosts.push(parsedHost);\n\n // --- サービス情報 ---\n const ports = ensureArray(host.ports?.port);\n const services: ParsedService[] = [];\n\n for (const port of ports) {\n const service = processPort(port, authority);\n services.push(service);\n result.services.push(service);\n }\n\n // --- OS 情報 ---\n const osMatches = ensureArray(host.os?.osmatch);\n if (osMatches.length > 0) {\n processOsMatches(osMatches, authority, services, result);\n }\n}\n\n/**\n * 単一の port 要素を処理し、ParsedService を返す。\n */\nfunction processPort(port: NmapPort, hostAuthority: string): ParsedService {\n const service = port.service;\n const serviceName = service?.['@_name'] ?? '';\n\n // appProto の決定: https or tunnel=ssl -> 'https'\n const appProto =\n service !== undefined && isHttps(service) ? 'https' : serviceName;\n\n const banner = service !== undefined ? buildBanner(service) : undefined;\n const protoConfidence = toProtoConfidence(service?.['@_conf']);\n\n return {\n hostAuthority,\n transport: port['@_protocol'],\n port: Number(port['@_portid']),\n appProto,\n protoConfidence,\n banner,\n product: service?.['@_product'],\n version: service?.['@_version'],\n state: port.state['@_state'],\n };\n}\n\n/**\n * OS マッチ情報を serviceObservations に追加する。\n * 最初のサービスの transport/port を使う。サービスがない場合は port=0, transport='tcp' を使う。\n */\nfunction processOsMatches(\n osMatches: NmapOsMatch[],\n hostAuthority: string,\n services: ParsedService[],\n result: ParseResult,\n): void {\n const firstService = services.length > 0 ? services[0] : undefined;\n const transport = firstService?.transport ?? 'tcp';\n const port = firstService?.port ?? 0;\n\n for (const osMatch of osMatches) {\n const observation: ParsedServiceObservation = {\n hostAuthority,\n transport,\n port,\n key: 'os',\n value: osMatch['@_name'],\n confidence: toOsConfidence(osMatch['@_accuracy']),\n };\n result.serviceObservations.push(observation);\n }\n}\n","/**\n * sonobat — Parser intermediate types\n *\n * パーサーは DB の ID を持たない中間表現を返す。\n * Normalizer が自然キー(authority, port 等)で DB を検索・upsert する。\n */\n\n// ============================================================\n// 中間表現(DB ID を持たない)\n// ============================================================\n\n/** ホストの中間表現 */\nexport interface ParsedHost {\n authority: string;\n authorityKind: 'IP' | 'DOMAIN';\n resolvedIps?: string[];\n}\n\n/** サービスの中間表現 */\nexport interface ParsedService {\n hostAuthority: string;\n transport: string;\n port: number;\n appProto: string;\n protoConfidence: string;\n banner?: string;\n product?: string;\n version?: string;\n state: string;\n}\n\n/** サービス観測の中間表現 */\nexport interface ParsedServiceObservation {\n hostAuthority: string;\n transport: string;\n port: number;\n key: string;\n value: string;\n confidence: string;\n}\n\n/** HTTP エンドポイントの中間表現 */\nexport interface ParsedHttpEndpoint {\n hostAuthority: string;\n port: number;\n baseUri: string;\n method: string;\n path: string;\n statusCode?: number;\n contentLength?: number;\n words?: number;\n lines?: number;\n}\n\n/** 入力パラメータの中間表現 */\nexport interface ParsedInput {\n hostAuthority: string;\n port: number;\n location: string;\n name: string;\n typeHint?: string;\n}\n\n/** エンドポイント ↔ 入力の紐づけ中間表現 */\nexport interface ParsedEndpointInput {\n hostAuthority: string;\n port: number;\n method: string;\n path: string;\n inputLocation: string;\n inputName: string;\n}\n\n/** 観測値の中間表現 */\nexport interface ParsedObservation {\n hostAuthority: string;\n port: number;\n inputLocation: string;\n inputName: string;\n rawValue: string;\n normValue: string;\n source: string;\n confidence: string;\n}\n\n/** 脆弱性の中間表現 */\nexport interface ParsedVulnerability {\n hostAuthority: string;\n port: number;\n method?: string;\n path?: string;\n vulnType: string;\n title: string;\n description?: string;\n severity: string;\n confidence: string;\n}\n\n/** CVE の中間表現 */\nexport interface ParsedCve {\n /** 対応する脆弱性の title(紐づけ用) */\n vulnerabilityTitle: string;\n cveId: string;\n description?: string;\n cvssScore?: number;\n cvssVector?: string;\n referenceUrl?: string;\n}\n\n// ============================================================\n// パース結果\n// ============================================================\n\n/** パーサーが返す統一的な結果型 */\nexport interface ParseResult {\n hosts: ParsedHost[];\n services: ParsedService[];\n serviceObservations: ParsedServiceObservation[];\n httpEndpoints: ParsedHttpEndpoint[];\n inputs: ParsedInput[];\n endpointInputs: ParsedEndpointInput[];\n observations: ParsedObservation[];\n vulnerabilities: ParsedVulnerability[];\n cves: ParsedCve[];\n}\n\n/** 空の ParseResult を生成するユーティリティ */\nexport function emptyParseResult(): ParseResult {\n return {\n hosts: [],\n services: [],\n serviceObservations: [],\n httpEndpoints: [],\n inputs: [],\n endpointInputs: [],\n observations: [],\n vulnerabilities: [],\n cves: [],\n };\n}\n","/**\n * sonobat — ffuf JSON output parser\n *\n * ffuf の JSON 出力をパースし、ParseResult に変換する。\n * パスディスカバリ、パラメータファジングの両方に対応。\n */\n\nimport type {\n ParseResult,\n ParsedHost,\n ParsedService,\n ParsedHttpEndpoint,\n ParsedInput,\n ParsedEndpointInput,\n ParsedObservation,\n} from '../types/parser.js';\nimport { emptyParseResult } from '../types/parser.js';\n\n// ---------------------------------------------------------------------------\n// 内部型: ffuf JSON 構造のバリデーション用\n// ---------------------------------------------------------------------------\n\ninterface FfufConfig {\n url: string;\n method: string;\n}\n\ninterface FfufResult {\n input: Record<string, string>;\n status: number;\n length: number;\n words: number;\n lines: number;\n url: string;\n host: string;\n}\n\ninterface FfufJson {\n commandline: string;\n config: FfufConfig;\n results: FfufResult[];\n}\n\n// ---------------------------------------------------------------------------\n// バリデーション\n// ---------------------------------------------------------------------------\n\nconst IP_REGEX = /^\\d{1,3}(\\.\\d{1,3}){3}$/;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction validateFfufJson(raw: unknown): FfufJson {\n if (!isRecord(raw)) {\n throw new Error('ffuf JSON: root must be an object');\n }\n\n if (typeof raw['commandline'] !== 'string') {\n throw new Error('ffuf JSON: commandline must be a string');\n }\n\n const config = raw['config'];\n if (!isRecord(config)) {\n throw new Error('ffuf JSON: config must be an object');\n }\n if (typeof config['url'] !== 'string' || typeof config['method'] !== 'string') {\n throw new Error('ffuf JSON: config.url and config.method must be strings');\n }\n\n if (!Array.isArray(raw['results'])) {\n throw new Error('ffuf JSON: results must be an array');\n }\n\n const results: FfufResult[] = [];\n for (const item of raw['results'] as unknown[]) {\n if (!isRecord(item)) {\n throw new Error('ffuf JSON: each result must be an object');\n }\n results.push({\n input: isRecord(item['input'])\n ? Object.fromEntries(\n Object.entries(item['input'] as Record<string, unknown>).map(([k, v]) => [\n k,\n String(v),\n ]),\n )\n : {},\n status: typeof item['status'] === 'number' ? item['status'] : 0,\n length: typeof item['length'] === 'number' ? item['length'] : 0,\n words: typeof item['words'] === 'number' ? item['words'] : 0,\n lines: typeof item['lines'] === 'number' ? item['lines'] : 0,\n url: typeof item['url'] === 'string' ? item['url'] : '',\n host: typeof item['host'] === 'string' ? item['host'] : '',\n });\n }\n\n return {\n commandline: raw['commandline'] as string,\n config: {\n url: config['url'] as string,\n method: config['method'] as string,\n },\n results,\n };\n}\n\n// ---------------------------------------------------------------------------\n// URL ユーティリティ\n// ---------------------------------------------------------------------------\n\ninterface ParsedUrl {\n scheme: string;\n hostname: string;\n port: number;\n pathname: string;\n searchParams: URLSearchParams;\n}\n\nfunction parseUrl(urlStr: string): ParsedUrl {\n const parsed = new URL(urlStr);\n const scheme = parsed.protocol.replace(':', '');\n\n let port: number;\n if (parsed.port !== '') {\n port = Number(parsed.port);\n } else {\n port = scheme === 'https' ? 443 : 80;\n }\n\n return {\n scheme,\n hostname: parsed.hostname,\n port,\n pathname: parsed.pathname,\n searchParams: parsed.searchParams,\n };\n}\n\nfunction determineAuthorityKind(hostname: string): 'IP' | 'DOMAIN' {\n return IP_REGEX.test(hostname) ? 'IP' : 'DOMAIN';\n}\n\n// ---------------------------------------------------------------------------\n// メインパーサー\n// ---------------------------------------------------------------------------\n\n/**\n * ffuf の JSON 出力文字列をパースし、ParseResult を返す。\n *\n * @param jsonContent - ffuf が `-of json` で出力した JSON 文字列\n * @returns ParseResult\n */\nexport function parseFfufJson(jsonContent: string): ParseResult {\n const raw: unknown = JSON.parse(jsonContent);\n const ffuf = validateFfufJson(raw);\n const method = ffuf.config.method;\n\n // results が空なら全て空配列\n if (ffuf.results.length === 0) {\n return emptyParseResult();\n }\n\n // ----- 集約用 Map -----\n // hosts: authority -> ParsedHost\n const hostsMap = new Map<string, ParsedHost>();\n // services: \"authority:port\" -> ParsedService\n const servicesMap = new Map<string, ParsedService>();\n // httpEndpoints: \"method:path\" -> ParsedHttpEndpoint\n const endpointsMap = new Map<string, ParsedHttpEndpoint>();\n // inputs: \"name\" -> ParsedInput (クエリパラメータ名で重複排除)\n const inputsMap = new Map<string, ParsedInput>();\n // endpointInputs: \"method:path:location:name\" -> ParsedEndpointInput\n const endpointInputsMap = new Map<string, ParsedEndpointInput>();\n // observations: \"location:name:rawValue\" -> ParsedObservation\n const observationsMap = new Map<string, ParsedObservation>();\n\n for (const result of ffuf.results) {\n if (result.url === '') {\n continue;\n }\n\n const parsed = parseUrl(result.url);\n const { scheme, hostname, port, pathname, searchParams } = parsed;\n const authorityKind = determineAuthorityKind(hostname);\n const baseUri = `${scheme}://${hostname}:${port}`;\n\n // --- Host ---\n if (!hostsMap.has(hostname)) {\n hostsMap.set(hostname, {\n authority: hostname,\n authorityKind,\n });\n }\n\n // --- Service ---\n const serviceKey = `${hostname}:${port}`;\n if (!servicesMap.has(serviceKey)) {\n servicesMap.set(serviceKey, {\n hostAuthority: hostname,\n transport: 'tcp',\n port,\n appProto: scheme,\n protoConfidence: 'high',\n state: 'open',\n });\n }\n\n // --- HTTP Endpoint (method + path で重複排除) ---\n const endpointKey = `${method}:${pathname}`;\n if (!endpointsMap.has(endpointKey)) {\n endpointsMap.set(endpointKey, {\n hostAuthority: hostname,\n port,\n baseUri,\n method,\n path: pathname,\n statusCode: result.status,\n contentLength: result.length,\n words: result.words,\n lines: result.lines,\n });\n }\n\n // --- Query Parameters -> inputs, observations, endpointInputs ---\n for (const [paramName, paramValue] of searchParams.entries()) {\n // Input (パラメータ名で重複排除)\n if (!inputsMap.has(paramName)) {\n inputsMap.set(paramName, {\n hostAuthority: hostname,\n port,\n location: 'query',\n name: paramName,\n });\n }\n\n // EndpointInput (endpoint + input の組み合わせで重複排除)\n const eiKey = `${method}:${pathname}:query:${paramName}`;\n if (!endpointInputsMap.has(eiKey)) {\n endpointInputsMap.set(eiKey, {\n hostAuthority: hostname,\n port,\n method,\n path: pathname,\n inputLocation: 'query',\n inputName: paramName,\n });\n }\n\n // Observation (パラメータ名 + 値で重複排除)\n const obsKey = `query:${paramName}:${paramValue}`;\n if (!observationsMap.has(obsKey)) {\n observationsMap.set(obsKey, {\n hostAuthority: hostname,\n port,\n inputLocation: 'query',\n inputName: paramName,\n rawValue: paramValue,\n normValue: paramValue,\n source: 'ffuf_url',\n confidence: 'high',\n });\n }\n }\n }\n\n return {\n hosts: [...hostsMap.values()],\n services: [...servicesMap.values()],\n serviceObservations: [],\n httpEndpoints: [...endpointsMap.values()],\n inputs: [...inputsMap.values()],\n endpointInputs: [...endpointInputsMap.values()],\n observations: [...observationsMap.values()],\n vulnerabilities: [],\n cves: [],\n };\n}\n","/**\r\n * sonobat — Nuclei JSONL パーサー\r\n *\r\n * nuclei の JSONL 出力を解析し、ParseResult 中間表現を返す。\r\n * 各行は独立した JSON オブジェクト(nuclei finding)として処理する。\r\n */\r\n\r\nimport type {\r\n ParseResult,\r\n ParsedHost,\r\n ParsedService,\r\n ParsedHttpEndpoint,\r\n ParsedVulnerability,\r\n ParsedCve,\r\n} from '../types/parser.js';\r\nimport { emptyParseResult } from '../types/parser.js';\r\n\r\n// ============================================================\r\n// nuclei finding の型定義(unknown から安全に取り出すための構造)\r\n// ============================================================\r\n\r\ninterface NucleiClassification {\r\n 'cve-id'?: string[];\r\n 'cvss-metrics'?: string;\r\n 'cvss-score'?: number;\r\n}\r\n\r\ninterface NucleiInfo {\r\n name: string;\r\n severity: string;\r\n tags: string[];\r\n classification?: NucleiClassification;\r\n}\r\n\r\ninterface NucleiFinding {\r\n 'template-id': string;\r\n info: NucleiInfo;\r\n type: string;\r\n host: string;\r\n 'matched-at': string;\r\n ip: string;\r\n port: string;\r\n scheme: string;\r\n url: string;\r\n}\r\n\r\n// ============================================================\r\n// 型ガード\r\n// ============================================================\r\n\r\nfunction isRecord(value: unknown): value is Record<string, unknown> {\r\n return typeof value === 'object' && value !== null && !Array.isArray(value);\r\n}\r\n\r\nfunction isStringArray(value: unknown): value is string[] {\r\n return (\r\n Array.isArray(value) && value.every((item) => typeof item === 'string')\r\n );\r\n}\r\n\r\nfunction isNucleiClassification(\r\n value: unknown,\r\n): value is NucleiClassification {\r\n if (!isRecord(value)) return false;\r\n // classification は空オブジェクトでも許容する\r\n if ('cve-id' in value && !isStringArray(value['cve-id'])) return false;\r\n if (\r\n 'cvss-metrics' in value &&\r\n typeof value['cvss-metrics'] !== 'string' &&\r\n value['cvss-metrics'] !== undefined\r\n )\r\n return false;\r\n if (\r\n 'cvss-score' in value &&\r\n typeof value['cvss-score'] !== 'number' &&\r\n value['cvss-score'] !== undefined\r\n )\r\n return false;\r\n return true;\r\n}\r\n\r\nfunction isNucleiInfo(value: unknown): value is NucleiInfo {\r\n if (!isRecord(value)) return false;\r\n if (typeof value.name !== 'string') return false;\r\n if (typeof value.severity !== 'string') return false;\r\n if (!isStringArray(value.tags)) return false;\r\n if (\r\n 'classification' in value &&\r\n value.classification !== undefined &&\r\n !isNucleiClassification(value.classification)\r\n )\r\n return false;\r\n return true;\r\n}\r\n\r\nfunction isNucleiFinding(value: unknown): value is NucleiFinding {\r\n if (!isRecord(value)) return false;\r\n if (typeof value['template-id'] !== 'string') return false;\r\n if (!isNucleiInfo(value.info)) return false;\r\n if (typeof value.type !== 'string') return false;\r\n if (typeof value.host !== 'string') return false;\r\n if (typeof value['matched-at'] !== 'string') return false;\r\n if (typeof value.ip !== 'string') return false;\r\n if (typeof value.port !== 'string') return false;\r\n if (typeof value.scheme !== 'string') return false;\r\n if (typeof value.url !== 'string') return false;\r\n return true;\r\n}\r\n\r\n// ============================================================\r\n// URL パス抽出(生文字列から、デコードせずに抽出する)\r\n// ============================================================\r\n\r\n/**\r\n * URL 文字列から pathname 部分を生文字列のまま抽出する。\r\n * Node.js の URL クラスは %2e を . にデコードしてパスを正規化してしまうため、\r\n * パストラバーサル系のペイロードを保存するには raw なパスが必要。\r\n */\r\nfunction extractRawPathname(urlStr: string): string {\r\n // scheme://authority の後のパス部分を取り出す\r\n // authority の終わり = 3つ目の / の位置(scheme://host:port/path...)\r\n const schemeEnd = urlStr.indexOf('://');\r\n if (schemeEnd === -1) {\r\n return '/';\r\n }\r\n const afterAuthority = urlStr.indexOf('/', schemeEnd + 3);\r\n if (afterAuthority === -1) {\r\n return '/';\r\n }\r\n // パスの終わり = ? または # の最初の出現位置\r\n const queryStart = urlStr.indexOf('?', afterAuthority);\r\n const fragmentStart = urlStr.indexOf('#', afterAuthority);\r\n let pathEnd = urlStr.length;\r\n if (queryStart !== -1 && queryStart < pathEnd) {\r\n pathEnd = queryStart;\r\n }\r\n if (fragmentStart !== -1 && fragmentStart < pathEnd) {\r\n pathEnd = fragmentStart;\r\n }\r\n return urlStr.substring(afterAuthority, pathEnd);\r\n}\r\n\r\n// ============================================================\r\n// vulnType 推定\r\n// ============================================================\r\n\r\n/** タグ配列から vulnType を推定する。優先度順に判定する。 */\r\nfunction inferVulnType(tags: string[]): string {\r\n const priorityTags = ['sqli', 'xss', 'rce', 'lfi', 'ssrf'] as const;\r\n for (const tag of priorityTags) {\r\n if (tags.includes(tag)) {\r\n return tag;\r\n }\r\n }\r\n return 'other';\r\n}\r\n\r\n// ============================================================\r\n// メインパーサー\r\n// ============================================================\r\n\r\n/**\r\n * nuclei JSONL 出力をパースし、ParseResult を返す。\r\n *\r\n * @param jsonl - nuclei の JSONL 出力文字列(1行1JSON)\r\n * @returns ParseResult 中間表現\r\n */\r\nexport function parseNucleiJsonl(jsonl: string): ParseResult {\r\n const result = emptyParseResult();\r\n\r\n if (jsonl.trim() === '') {\r\n return result;\r\n }\r\n\r\n const lines = jsonl.split('\\n');\r\n const seenHosts = new Set<string>();\r\n const seenServices = new Set<string>();\r\n\r\n for (const line of lines) {\r\n const trimmed = line.trim();\r\n if (trimmed === '') {\r\n continue;\r\n }\r\n\r\n const parsed: unknown = JSON.parse(trimmed);\r\n if (!isNucleiFinding(parsed)) {\r\n continue;\r\n }\r\n\r\n processFinding(parsed, result, seenHosts, seenServices);\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 単一の nuclei finding を処理し、結果に追加する。\r\n */\r\nfunction processFinding(\r\n finding: NucleiFinding,\r\n result: ParseResult,\r\n seenHosts: Set<string>,\r\n seenServices: Set<string>,\r\n): void {\r\n const ip = finding.ip;\r\n const port = Number(finding.port);\r\n const scheme = finding.scheme;\r\n const matchedAt = finding['matched-at'];\r\n\r\n // --- ホスト(重複排除) ---\r\n if (!seenHosts.has(ip)) {\r\n seenHosts.add(ip);\r\n const host: ParsedHost = {\r\n authority: ip,\r\n authorityKind: 'IP',\r\n };\r\n result.hosts.push(host);\r\n }\r\n\r\n // --- サービス(authority+port で重複排除) ---\r\n const serviceKey = `${ip}:${port}`;\r\n if (!seenServices.has(serviceKey)) {\r\n seenServices.add(serviceKey);\r\n const service: ParsedService = {\r\n hostAuthority: ip,\r\n transport: 'tcp',\r\n port,\r\n appProto: scheme,\r\n protoConfidence: 'high',\r\n state: 'open',\r\n };\r\n result.services.push(service);\r\n }\r\n\r\n // --- HTTP エンドポイント ---\r\n const rawPath = extractRawPathname(matchedAt);\r\n const baseUri = `${scheme}://${ip}:${port}`;\r\n\r\n const endpoint: ParsedHttpEndpoint = {\r\n hostAuthority: ip,\r\n port,\r\n baseUri,\r\n method: 'GET',\r\n path: rawPath,\r\n };\r\n result.httpEndpoints.push(endpoint);\r\n\r\n // --- 脆弱性 ---\r\n const info = finding.info;\r\n const vulnerability: ParsedVulnerability = {\r\n hostAuthority: ip,\r\n port,\r\n method: 'GET',\r\n path: rawPath,\r\n vulnType: inferVulnType(info.tags),\r\n title: info.name,\r\n severity: info.severity,\r\n confidence: 'high',\r\n };\r\n result.vulnerabilities.push(vulnerability);\r\n\r\n // --- CVE ---\r\n const classification = info.classification;\r\n if (\r\n classification !== undefined &&\r\n isRecord(classification) &&\r\n 'cve-id' in classification &&\r\n isStringArray(classification['cve-id']) &&\r\n classification['cve-id'].length > 0\r\n ) {\r\n for (const cveId of classification['cve-id']) {\r\n const cve: ParsedCve = {\r\n vulnerabilityTitle: info.name,\r\n cveId,\r\n cvssScore: typeof classification['cvss-score'] === 'number' ? classification['cvss-score'] : undefined,\r\n cvssVector: typeof classification['cvss-metrics'] === 'string' ? classification['cvss-metrics'] : undefined,\r\n };\r\n result.cves.push(cve);\r\n }\r\n }\r\n}\r\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { ServiceObservation } from '../../types/entities.js';\nimport type { CreateServiceObservationInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `service_observations` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface ServiceObservationRow {\n id: string;\n service_id: string;\n key: string;\n value: string;\n confidence: string;\n evidence_artifact_id: string;\n created_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase ServiceObservation entity. */\nfunction rowToServiceObservation(row: ServiceObservationRow): ServiceObservation {\n return {\n id: row.id,\n serviceId: row.service_id,\n key: row.key,\n value: row.value,\n confidence: row.confidence,\n evidenceArtifactId: row.evidence_artifact_id,\n createdAt: row.created_at,\n };\n}\n\n/**\n * Repository for the `service_observations` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class ServiceObservationRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new ServiceObservation and return the full entity. */\n create(input: CreateServiceObservationInput): ServiceObservation {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string, string, string, string, string]\n >(\n `INSERT INTO service_observations (id, service_id, key, value, confidence, evidence_artifact_id, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.serviceId,\n input.key,\n input.value,\n input.confidence,\n input.evidenceArtifactId,\n now,\n );\n\n return {\n id,\n serviceId: input.serviceId,\n key: input.key,\n value: input.value,\n confidence: input.confidence,\n evidenceArtifactId: input.evidenceArtifactId,\n createdAt: now,\n };\n }\n\n /** Find a ServiceObservation by its primary key. Returns undefined if not found. */\n findById(id: string): ServiceObservation | undefined {\n const stmt = this.db.prepare<[string], ServiceObservationRow>(\n `SELECT id, service_id, key, value, confidence, evidence_artifact_id, created_at\n FROM service_observations\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToServiceObservation(row) : undefined;\n }\n\n /** Return all ServiceObservations for a given service. */\n findByServiceId(serviceId: string): ServiceObservation[] {\n const stmt = this.db.prepare<[string], ServiceObservationRow>(\n `SELECT id, service_id, key, value, confidence, evidence_artifact_id, created_at\n FROM service_observations\n WHERE service_id = ?`,\n );\n\n const rows = stmt.all(serviceId);\n return rows.map(rowToServiceObservation);\n }\n\n /** Delete a ServiceObservation by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM service_observations WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { EndpointInput } from '../../types/entities.js';\nimport type { CreateEndpointInputInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `endpoint_inputs` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface EndpointInputRow {\n id: string;\n endpoint_id: string;\n input_id: string;\n evidence_artifact_id: string;\n created_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase EndpointInput entity. */\nfunction rowToEndpointInput(row: EndpointInputRow): EndpointInput {\n return {\n id: row.id,\n endpointId: row.endpoint_id,\n inputId: row.input_id,\n evidenceArtifactId: row.evidence_artifact_id,\n createdAt: row.created_at,\n };\n}\n\n/**\n * Repository for the `endpoint_inputs` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class EndpointInputRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new EndpointInput and return the full entity. */\n create(input: CreateEndpointInputInput): EndpointInput {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string, string, string]\n >(\n `INSERT INTO endpoint_inputs (id, endpoint_id, input_id, evidence_artifact_id, created_at)\n VALUES (?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.endpointId,\n input.inputId,\n input.evidenceArtifactId,\n now,\n );\n\n return {\n id,\n endpointId: input.endpointId,\n inputId: input.inputId,\n evidenceArtifactId: input.evidenceArtifactId,\n createdAt: now,\n };\n }\n\n /** Find an EndpointInput by its primary key. Returns undefined if not found. */\n findById(id: string): EndpointInput | undefined {\n const stmt = this.db.prepare<[string], EndpointInputRow>(\n `SELECT id, endpoint_id, input_id, evidence_artifact_id, created_at\n FROM endpoint_inputs\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToEndpointInput(row) : undefined;\n }\n\n /** Find all EndpointInputs for a given endpoint. */\n findByEndpointId(endpointId: string): EndpointInput[] {\n const stmt = this.db.prepare<[string], EndpointInputRow>(\n `SELECT id, endpoint_id, input_id, evidence_artifact_id, created_at\n FROM endpoint_inputs\n WHERE endpoint_id = ?`,\n );\n\n const rows = stmt.all(endpointId);\n return rows.map(rowToEndpointInput);\n }\n\n /** Find all EndpointInputs for a given input. */\n findByInputId(inputId: string): EndpointInput[] {\n const stmt = this.db.prepare<[string], EndpointInputRow>(\n `SELECT id, endpoint_id, input_id, evidence_artifact_id, created_at\n FROM endpoint_inputs\n WHERE input_id = ?`,\n );\n\n const rows = stmt.all(inputId);\n return rows.map(rowToEndpointInput);\n }\n\n /** Delete an EndpointInput by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM endpoint_inputs WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { Cve } from '../../types/entities.js';\nimport type { CreateCveInput } from '../../types/repository.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `cves` table.\n * Column names are snake_case as defined in the schema.\n */\ninterface CveRow {\n id: string;\n vulnerability_id: string;\n cve_id: string;\n description: string | null;\n cvss_score: number | null;\n cvss_vector: string | null;\n reference_url: string | null;\n created_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase Cve entity. */\nfunction rowToCve(row: CveRow): Cve {\n return {\n id: row.id,\n vulnerabilityId: row.vulnerability_id,\n cveId: row.cve_id,\n description: row.description ?? undefined,\n cvssScore: row.cvss_score ?? undefined,\n cvssVector: row.cvss_vector ?? undefined,\n referenceUrl: row.reference_url ?? undefined,\n createdAt: row.created_at,\n };\n}\n\n/**\n * Repository for the `cves` table.\n *\n * All queries use prepared statements to prevent SQL injection.\n */\nexport class CveRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new Cve and return the full entity. */\n create(input: CreateCveInput): Cve {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string, string | null, number | null, string | null, string | null, string]\n >(\n `INSERT INTO cves (id, vulnerability_id, cve_id, description, cvss_score, cvss_vector, reference_url, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.vulnerabilityId,\n input.cveId,\n input.description ?? null,\n input.cvssScore ?? null,\n input.cvssVector ?? null,\n input.referenceUrl ?? null,\n now,\n );\n\n return {\n id,\n vulnerabilityId: input.vulnerabilityId,\n cveId: input.cveId,\n description: input.description,\n cvssScore: input.cvssScore,\n cvssVector: input.cvssVector,\n referenceUrl: input.referenceUrl,\n createdAt: now,\n };\n }\n\n /** Find a Cve by its primary key. Returns undefined if not found. */\n findById(id: string): Cve | undefined {\n const stmt = this.db.prepare<[string], CveRow>(\n `SELECT id, vulnerability_id, cve_id, description, cvss_score, cvss_vector, reference_url, created_at\n FROM cves\n WHERE id = ?`,\n );\n\n const row = stmt.get(id);\n return row ? rowToCve(row) : undefined;\n }\n\n /** Return all Cves associated with a given vulnerability. */\n findByVulnerabilityId(vulnerabilityId: string): Cve[] {\n const stmt = this.db.prepare<[string], CveRow>(\n `SELECT id, vulnerability_id, cve_id, description, cvss_score, cvss_vector, reference_url, created_at\n FROM cves\n WHERE vulnerability_id = ?`,\n );\n\n const rows = stmt.all(vulnerabilityId);\n return rows.map(rowToCve);\n }\n\n /** Delete a Cve by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM cves WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","/**\n * sonobat — Normalizer\n *\n * ParseResult(パーサーの中間表現)を受け取り、\n * 自然キーで既存レコードを検索(upsert)しながら DB に書き込む。\n * 全操作はトランザクションでラップし、アトミックに実行する。\n */\n\nimport type Database from 'better-sqlite3';\nimport type { ParseResult } from '../types/parser.js';\nimport { HostRepository } from '../db/repository/host-repository.js';\nimport { ServiceRepository } from '../db/repository/service-repository.js';\nimport { ServiceObservationRepository } from '../db/repository/service-observation-repository.js';\nimport { HttpEndpointRepository } from '../db/repository/http-endpoint-repository.js';\nimport { InputRepository } from '../db/repository/input-repository.js';\nimport { EndpointInputRepository } from '../db/repository/endpoint-input-repository.js';\nimport { ObservationRepository } from '../db/repository/observation-repository.js';\nimport { VulnerabilityRepository } from '../db/repository/vulnerability-repository.js';\nimport { CveRepository } from '../db/repository/cve-repository.js';\n\n// ============================================================\n// 結果型\n// ============================================================\n\n/** normalize() の実行結果。各エンティティの新規作成数を返す。 */\nexport interface NormalizeResult {\n hostsCreated: number;\n servicesCreated: number;\n serviceObservationsCreated: number;\n httpEndpointsCreated: number;\n inputsCreated: number;\n endpointInputsCreated: number;\n observationsCreated: number;\n vulnerabilitiesCreated: number;\n cvesCreated: number;\n}\n\n// ============================================================\n// normalize\n// ============================================================\n\n/**\n * ParseResult を DB に正規化して書き込む。\n *\n * - 自然キー(authority, host_id+transport+port 等)で既存レコードを検索し、\n * 存在すれば再利用、なければ新規作成する(upsert パターン)。\n * - 全操作は 1 トランザクション内で実行される。\n *\n * @param db better-sqlite3 の Database インスタンス\n * @param artifactId 根拠となる Artifact の ID(evidence_artifact_id に設定)\n * @param parseResult パーサーが返した中間表現\n * @returns 各エンティティの新規作成数\n */\nexport function normalize(\n db: Database.Database,\n artifactId: string,\n parseResult: ParseResult,\n): NormalizeResult {\n const hostRepo = new HostRepository(db);\n const serviceRepo = new ServiceRepository(db);\n const serviceObsRepo = new ServiceObservationRepository(db);\n const httpEndpointRepo = new HttpEndpointRepository(db);\n const inputRepo = new InputRepository(db);\n const endpointInputRepo = new EndpointInputRepository(db);\n const observationRepo = new ObservationRepository(db);\n const vulnRepo = new VulnerabilityRepository(db);\n const cveRepo = new CveRepository(db);\n\n const run = db.transaction((): NormalizeResult => {\n const result: NormalizeResult = {\n hostsCreated: 0,\n servicesCreated: 0,\n serviceObservationsCreated: 0,\n httpEndpointsCreated: 0,\n inputsCreated: 0,\n endpointInputsCreated: 0,\n observationsCreated: 0,\n vulnerabilitiesCreated: 0,\n cvesCreated: 0,\n };\n\n // ---------------------------------------------------------\n // 自然キー → DB ID のマッピング\n // ---------------------------------------------------------\n const hostIdByAuthority = new Map<string, string>();\n // key: \"hostId:transport:port\"\n const serviceIdByKey = new Map<string, string>();\n // key: \"serviceId:method:path\"\n const endpointIdByKey = new Map<string, string>();\n // key: \"serviceId:location:name\"\n const inputIdByKey = new Map<string, string>();\n // key: vulnerability title → DB ID\n const vulnIdByTitle = new Map<string, string>();\n\n // ---------------------------------------------------------\n // 1. Upsert hosts\n // ---------------------------------------------------------\n for (const parsed of parseResult.hosts) {\n const existing = hostRepo.findByAuthority(parsed.authority);\n if (existing) {\n hostIdByAuthority.set(parsed.authority, existing.id);\n } else {\n const host = hostRepo.create({\n authorityKind: parsed.authorityKind,\n authority: parsed.authority,\n resolvedIpsJson: JSON.stringify(parsed.resolvedIps ?? []),\n });\n hostIdByAuthority.set(parsed.authority, host.id);\n result.hostsCreated++;\n }\n }\n\n // ---------------------------------------------------------\n // 2. Upsert services\n // ---------------------------------------------------------\n for (const parsed of parseResult.services) {\n const hostId = hostIdByAuthority.get(parsed.hostAuthority);\n if (!hostId) continue;\n\n const svcKey = `${hostId}:${parsed.transport}:${parsed.port}`;\n if (serviceIdByKey.has(svcKey)) continue;\n\n // DB から既存サービスを検索\n const existingServices = serviceRepo.findByHostId(hostId);\n const existing = existingServices.find(\n (s) => s.transport === parsed.transport && s.port === parsed.port,\n );\n\n if (existing) {\n serviceIdByKey.set(svcKey, existing.id);\n } else {\n const service = serviceRepo.create({\n hostId,\n transport: parsed.transport,\n port: parsed.port,\n appProto: parsed.appProto,\n protoConfidence: parsed.protoConfidence,\n banner: parsed.banner,\n product: parsed.product,\n version: parsed.version,\n state: parsed.state,\n evidenceArtifactId: artifactId,\n });\n serviceIdByKey.set(svcKey, service.id);\n result.servicesCreated++;\n }\n }\n\n // ---------------------------------------------------------\n // ヘルパー: hostAuthority + port → serviceId を解決\n // HTTP 系エンティティは transport が常に tcp\n // ---------------------------------------------------------\n function resolveServiceId(hostAuthority: string, port: number): string | undefined {\n const hostId = hostIdByAuthority.get(hostAuthority);\n if (!hostId) return undefined;\n return serviceIdByKey.get(`${hostId}:tcp:${port}`);\n }\n\n // ---------------------------------------------------------\n // 3. Service observations\n // ---------------------------------------------------------\n for (const parsed of parseResult.serviceObservations) {\n const hostId = hostIdByAuthority.get(parsed.hostAuthority);\n if (!hostId) continue;\n\n const svcKey = `${hostId}:${parsed.transport}:${parsed.port}`;\n const serviceId = serviceIdByKey.get(svcKey);\n if (!serviceId) continue;\n\n serviceObsRepo.create({\n serviceId,\n key: parsed.key,\n value: parsed.value,\n confidence: parsed.confidence,\n evidenceArtifactId: artifactId,\n });\n result.serviceObservationsCreated++;\n }\n\n // ---------------------------------------------------------\n // 4. Upsert HTTP endpoints\n // ---------------------------------------------------------\n for (const parsed of parseResult.httpEndpoints) {\n const serviceId = resolveServiceId(parsed.hostAuthority, parsed.port);\n if (!serviceId) continue;\n\n const epKey = `${serviceId}:${parsed.method}:${parsed.path}`;\n if (endpointIdByKey.has(epKey)) continue;\n\n const existingEndpoints = httpEndpointRepo.findByServiceId(serviceId);\n const existing = existingEndpoints.find(\n (e) => e.method === parsed.method && e.path === parsed.path,\n );\n\n if (existing) {\n endpointIdByKey.set(epKey, existing.id);\n } else {\n const endpoint = httpEndpointRepo.create({\n serviceId,\n baseUri: parsed.baseUri,\n method: parsed.method,\n path: parsed.path,\n statusCode: parsed.statusCode,\n contentLength: parsed.contentLength,\n words: parsed.words,\n lines: parsed.lines,\n evidenceArtifactId: artifactId,\n });\n endpointIdByKey.set(epKey, endpoint.id);\n result.httpEndpointsCreated++;\n }\n }\n\n // ---------------------------------------------------------\n // 5. Upsert inputs\n // ---------------------------------------------------------\n for (const parsed of parseResult.inputs) {\n const serviceId = resolveServiceId(parsed.hostAuthority, parsed.port);\n if (!serviceId) continue;\n\n const inKey = `${serviceId}:${parsed.location}:${parsed.name}`;\n if (inputIdByKey.has(inKey)) continue;\n\n const existingInputs = inputRepo.findByServiceId(serviceId);\n const existing = existingInputs.find(\n (i) => i.location === parsed.location && i.name === parsed.name,\n );\n\n if (existing) {\n inputIdByKey.set(inKey, existing.id);\n } else {\n const input = inputRepo.create({\n serviceId,\n location: parsed.location,\n name: parsed.name,\n typeHint: parsed.typeHint,\n });\n inputIdByKey.set(inKey, input.id);\n result.inputsCreated++;\n }\n }\n\n // ---------------------------------------------------------\n // 6. Upsert endpoint_inputs\n // ---------------------------------------------------------\n for (const parsed of parseResult.endpointInputs) {\n const serviceId = resolveServiceId(parsed.hostAuthority, parsed.port);\n if (!serviceId) continue;\n\n const epKey = `${serviceId}:${parsed.method}:${parsed.path}`;\n const endpointId = endpointIdByKey.get(epKey);\n if (!endpointId) continue;\n\n const inKey = `${serviceId}:${parsed.inputLocation}:${parsed.inputName}`;\n const inputId = inputIdByKey.get(inKey);\n if (!inputId) continue;\n\n // 既存リンクの確認\n const existingLinks = endpointInputRepo.findByEndpointId(endpointId);\n const alreadyLinked = existingLinks.some((l) => l.inputId === inputId);\n if (alreadyLinked) continue;\n\n endpointInputRepo.create({\n endpointId,\n inputId,\n evidenceArtifactId: artifactId,\n });\n result.endpointInputsCreated++;\n }\n\n // ---------------------------------------------------------\n // 7. Observations(常に新規作成)\n // ---------------------------------------------------------\n for (const parsed of parseResult.observations) {\n const serviceId = resolveServiceId(parsed.hostAuthority, parsed.port);\n if (!serviceId) continue;\n\n const inKey = `${serviceId}:${parsed.inputLocation}:${parsed.inputName}`;\n const inputId = inputIdByKey.get(inKey);\n if (!inputId) continue;\n\n observationRepo.create({\n inputId,\n rawValue: parsed.rawValue,\n normValue: parsed.normValue,\n source: parsed.source,\n confidence: parsed.confidence,\n evidenceArtifactId: artifactId,\n observedAt: new Date().toISOString(),\n });\n result.observationsCreated++;\n }\n\n // ---------------------------------------------------------\n // 8. Vulnerabilities\n // ---------------------------------------------------------\n for (const parsed of parseResult.vulnerabilities) {\n const serviceId = resolveServiceId(parsed.hostAuthority, parsed.port);\n if (!serviceId) continue;\n\n // endpoint への紐づけ(任意)\n let endpointId: string | undefined;\n if (parsed.method && parsed.path) {\n const epKey = `${serviceId}:${parsed.method}:${parsed.path}`;\n endpointId = endpointIdByKey.get(epKey);\n }\n\n const vuln = vulnRepo.create({\n serviceId,\n endpointId,\n vulnType: parsed.vulnType,\n title: parsed.title,\n description: parsed.description,\n severity: parsed.severity,\n confidence: parsed.confidence,\n evidenceArtifactId: artifactId,\n });\n vulnIdByTitle.set(parsed.title, vuln.id);\n result.vulnerabilitiesCreated++;\n }\n\n // ---------------------------------------------------------\n // 9. CVEs\n // ---------------------------------------------------------\n for (const parsed of parseResult.cves) {\n const vulnId = vulnIdByTitle.get(parsed.vulnerabilityTitle);\n if (!vulnId) continue;\n\n cveRepo.create({\n vulnerabilityId: vulnId,\n cveId: parsed.cveId,\n description: parsed.description,\n cvssScore: parsed.cvssScore,\n cvssVector: parsed.cvssVector,\n referenceUrl: parsed.referenceUrl,\n });\n result.cvesCreated++;\n }\n\n return result;\n });\n\n return run();\n}\n","/**\n * sonobat — MCP Propose Tool\n *\n * Tool for generating next-step action proposals based on missing data.\n */\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type Database from 'better-sqlite3';\nimport { z } from 'zod';\nimport { propose } from '../../engine/proposer.js';\n\nexport function registerProposeTool(server: McpServer, db: Database.Database): void {\n server.tool(\n 'propose',\n 'Analyze the AttackDataGraph for missing data and propose next-step actions',\n {\n hostId: z.string().optional().describe('Limit proposals to a specific host (optional)'),\n },\n async ({ hostId }) => {\n const actions = propose(db, hostId);\n if (actions.length === 0) {\n return {\n content: [{ type: 'text', text: 'No actions proposed. All discovered data appears complete.' }],\n };\n }\n return { content: [{ type: 'text', text: JSON.stringify(actions, null, 2) }] };\n },\n );\n}\n","/**\r\n * sonobat — Proposer engine\r\n *\r\n * Analyzes the AttackDataGraph stored in SQLite and proposes\r\n * next-step actions (scans, discovery, etc.) based on missing data.\r\n */\r\n\r\nimport type Database from 'better-sqlite3';\r\nimport type { Action } from '../types/engine.js';\r\nimport { HostRepository } from '../db/repository/host-repository.js';\r\nimport { ServiceRepository } from '../db/repository/service-repository.js';\r\nimport { HttpEndpointRepository } from '../db/repository/http-endpoint-repository.js';\r\nimport { InputRepository } from '../db/repository/input-repository.js';\r\nimport { EndpointInputRepository } from '../db/repository/endpoint-input-repository.js';\r\nimport { ObservationRepository } from '../db/repository/observation-repository.js';\r\nimport { VhostRepository } from '../db/repository/vhost-repository.js';\r\nimport { VulnerabilityRepository } from '../db/repository/vulnerability-repository.js';\r\nimport type { Host, Service } from '../types/entities.js';\r\n\r\n/**\r\n * Analyze the database for missing reconnaissance data and return\r\n * a list of proposed actions to fill the gaps.\r\n *\r\n * @param db - The better-sqlite3 database instance\r\n * @param hostId - Optional: limit analysis to a single host\r\n * @returns Array of proposed actions\r\n */\r\nexport function propose(db: Database.Database, hostId?: string): Action[] {\r\n const hostRepo = new HostRepository(db);\r\n const serviceRepo = new ServiceRepository(db);\r\n const httpEndpointRepo = new HttpEndpointRepository(db);\r\n const inputRepo = new InputRepository(db);\r\n const endpointInputRepo = new EndpointInputRepository(db);\r\n const observationRepo = new ObservationRepository(db);\r\n const vhostRepo = new VhostRepository(db);\r\n const vulnRepo = new VulnerabilityRepository(db);\r\n\r\n const actions: Action[] = [];\r\n\r\n // Determine target hosts\r\n let hosts: Host[];\r\n if (hostId !== undefined) {\r\n const host = hostRepo.findById(hostId);\r\n if (host === undefined) {\r\n return [];\r\n }\r\n hosts = [host];\r\n } else {\r\n hosts = hostRepo.findAll();\r\n }\r\n\r\n for (const host of hosts) {\r\n const services = serviceRepo.findByHostId(host.id);\r\n\r\n // (a) No services at all -> suggest nmap scan\r\n if (services.length === 0) {\r\n actions.push({\r\n kind: 'nmap_scan',\r\n description: `Port scan ${host.authority} to discover services`,\r\n command: `nmap -p- -sV ${host.authority}`,\r\n params: { hostId: host.id },\r\n });\r\n continue;\r\n }\r\n\r\n // (b) For each HTTP/HTTPS service, check for missing data\r\n for (const service of services) {\r\n if (service.appProto !== 'http' && service.appProto !== 'https') {\r\n continue;\r\n }\r\n\r\n const baseUri = `${service.appProto}://${host.authority}:${service.port}`;\r\n\r\n proposeForHttpService(\r\n actions,\r\n host,\r\n service,\r\n baseUri,\r\n httpEndpointRepo,\r\n inputRepo,\r\n endpointInputRepo,\r\n observationRepo,\r\n vhostRepo,\r\n vulnRepo,\r\n );\r\n }\r\n }\r\n\r\n return actions;\r\n}\r\n\r\n/**\r\n * Check a single HTTP/HTTPS service for missing data and push\r\n * proposed actions into the actions array.\r\n */\r\nfunction proposeForHttpService(\r\n actions: Action[],\r\n host: Host,\r\n service: Service,\r\n baseUri: string,\r\n httpEndpointRepo: HttpEndpointRepository,\r\n inputRepo: InputRepository,\r\n endpointInputRepo: EndpointInputRepository,\r\n observationRepo: ObservationRepository,\r\n vhostRepo: VhostRepository,\r\n vulnRepo: VulnerabilityRepository,\r\n): void {\r\n const endpoints = httpEndpointRepo.findByServiceId(service.id);\r\n\r\n // No endpoints -> suggest directory/file discovery\r\n if (endpoints.length === 0) {\r\n actions.push({\r\n kind: 'ffuf_discovery',\r\n description: `Discover endpoints on ${baseUri}`,\r\n command: `ffuf -u ${baseUri}/FUZZ -w /usr/share/wordlists/dirb/common.txt`,\r\n params: { hostId: host.id, serviceId: service.id },\r\n });\r\n }\r\n\r\n // Check vulnerabilities at service level (used for value_fuzz decision)\r\n const vulns = vulnRepo.findByServiceId(service.id);\r\n\r\n // For each endpoint: check inputs via endpoint_inputs (per-endpoint)\r\n for (const endpoint of endpoints) {\r\n const endpointInputs = endpointInputRepo.findByEndpointId(endpoint.id);\r\n\r\n if (endpointInputs.length === 0) {\r\n actions.push({\r\n kind: 'parameter_discovery',\r\n description: `Discover input parameters for ${baseUri}${endpoint.path}`,\r\n params: { hostId: host.id, serviceId: service.id, endpointId: endpoint.id },\r\n });\r\n }\r\n\r\n // For each linked input: check observations\r\n for (const ei of endpointInputs) {\r\n const input = inputRepo.findById(ei.inputId);\r\n if (input === undefined) {\r\n continue;\r\n }\r\n\r\n const observations = observationRepo.findByInputId(input.id);\r\n\r\n if (observations.length === 0) {\r\n actions.push({\r\n kind: 'value_collection',\r\n description: `Collect observed values for input \"${input.name}\" (${input.location})`,\r\n params: {\r\n hostId: host.id,\r\n serviceId: service.id,\r\n endpointId: endpoint.id,\r\n inputId: input.id,\r\n },\r\n });\r\n } else if (vulns.length === 0) {\r\n // Has input + observations, but no vulnerabilities → suggest fuzzing\r\n actions.push({\r\n kind: 'value_fuzz',\r\n description: `Fuzz input \"${input.name}\" (${input.location}) on ${baseUri}${endpoint.path}`,\r\n params: {\r\n hostId: host.id,\r\n serviceId: service.id,\r\n endpointId: endpoint.id,\r\n inputId: input.id,\r\n },\r\n });\r\n }\r\n }\r\n }\r\n\r\n // Check vhosts\r\n const vhosts = vhostRepo.findByHostId(host.id);\r\n if (vhosts.length === 0) {\r\n actions.push({\r\n kind: 'vhost_discovery',\r\n description: `Discover virtual hosts for ${host.authority}`,\r\n params: { hostId: host.id, serviceId: service.id },\r\n });\r\n }\r\n\r\n // Check vulnerabilities → suggest nuclei scan if none\r\n if (vulns.length === 0) {\r\n actions.push({\r\n kind: 'nuclei_scan',\r\n description: `Scan ${baseUri} for known vulnerabilities`,\r\n command: `nuclei -u ${baseUri} -jsonl`,\r\n params: { hostId: host.id, serviceId: service.id },\r\n });\r\n }\r\n}\r\n","/**\n * sonobat — MCP Mutation Tools\n *\n * Tools for manually adding data to the AttackDataGraph.\n */\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type Database from 'better-sqlite3';\nimport { z } from 'zod';\nimport { HostRepository } from '../../db/repository/host-repository.js';\nimport { ArtifactRepository } from '../../db/repository/artifact-repository.js';\nimport { CredentialRepository } from '../../db/repository/credential-repository.js';\nimport { VulnerabilityRepository } from '../../db/repository/vulnerability-repository.js';\nimport { CveRepository } from '../../db/repository/cve-repository.js';\n\n/**\n * Get or create a singleton \"manual\" artifact for manual data entry.\n * Reused across all manual mutations to avoid artifact proliferation.\n */\nfunction getOrCreateManualArtifact(db: Database.Database): string {\n const artifactRepo = new ArtifactRepository(db);\n const existing = artifactRepo.findByTool('manual');\n if (existing.length > 0) {\n return existing[0].id;\n }\n const artifact = artifactRepo.create({\n tool: 'manual',\n kind: 'manual_entry',\n path: 'manual',\n capturedAt: new Date().toISOString(),\n });\n return artifact.id;\n}\n\nexport function registerMutationTools(server: McpServer, db: Database.Database): void {\n // 1. add_host\n server.tool(\n 'add_host',\n 'Manually add a host to the AttackDataGraph',\n {\n authority: z.string().describe('IP address or domain name'),\n authorityKind: z.enum(['IP', 'DOMAIN']).describe('Type of authority'),\n },\n async ({ authority, authorityKind }) => {\n const hostRepo = new HostRepository(db);\n const existing = hostRepo.findByAuthority(authority);\n if (existing) {\n return {\n content: [{ type: 'text', text: `Host already exists: ${JSON.stringify(existing, null, 2)}` }],\n };\n }\n const host = hostRepo.create({\n authorityKind,\n authority,\n resolvedIpsJson: '[]',\n });\n return { content: [{ type: 'text', text: JSON.stringify(host, null, 2) }] };\n },\n );\n\n // 2. add_credential\n server.tool(\n 'add_credential',\n 'Manually add a credential for a service',\n {\n serviceId: z.string().describe('Service UUID'),\n username: z.string().describe('Username'),\n secret: z.string().describe('Secret value (password, token, etc.)'),\n secretType: z.enum(['password', 'token', 'api_key', 'ssh_key']).describe('Type of secret'),\n source: z.enum(['brute_force', 'default', 'leaked', 'manual']).describe('How the credential was obtained'),\n confidence: z.enum(['high', 'medium', 'low']).describe('Confidence level').default('medium'),\n },\n async ({ serviceId, username, secret, secretType, source, confidence }) => {\n const artifactId = getOrCreateManualArtifact(db);\n const credentialRepo = new CredentialRepository(db);\n const credential = credentialRepo.create({\n serviceId,\n username,\n secret,\n secretType,\n source,\n confidence,\n evidenceArtifactId: artifactId,\n });\n return { content: [{ type: 'text', text: JSON.stringify(credential, null, 2) }] };\n },\n );\n\n // 3. add_vulnerability\n server.tool(\n 'add_vulnerability',\n 'Manually add a vulnerability for a service',\n {\n serviceId: z.string().describe('Service UUID'),\n vulnType: z.string().describe('Vulnerability type (sqli, xss, rce, lfi, ssrf, etc.)'),\n title: z.string().describe('Vulnerability title'),\n severity: z.enum(['critical', 'high', 'medium', 'low', 'info']).describe('Severity level'),\n confidence: z.enum(['high', 'medium', 'low']).describe('Confidence level').default('medium'),\n endpointId: z.string().optional().describe('HTTP endpoint UUID (optional)'),\n description: z.string().optional().describe('Detailed description (optional)'),\n },\n async ({ serviceId, vulnType, title, severity, confidence, endpointId, description }) => {\n const artifactId = getOrCreateManualArtifact(db);\n const vulnRepo = new VulnerabilityRepository(db);\n const vuln = vulnRepo.create({\n serviceId,\n endpointId,\n vulnType,\n title,\n description,\n severity,\n confidence,\n evidenceArtifactId: artifactId,\n });\n return { content: [{ type: 'text', text: JSON.stringify(vuln, null, 2) }] };\n },\n );\n\n // 4. link_cve\n server.tool(\n 'link_cve',\n 'Link a CVE record to an existing vulnerability',\n {\n vulnerabilityId: z.string().describe('Vulnerability UUID'),\n cveId: z.string().describe('CVE identifier (e.g. CVE-2021-44228)'),\n description: z.string().optional().describe('CVE description'),\n cvssScore: z.number().optional().describe('CVSS score (0.0 - 10.0)'),\n cvssVector: z.string().optional().describe('CVSS vector string'),\n referenceUrl: z.string().optional().describe('Reference URL'),\n },\n async ({ vulnerabilityId, cveId, description, cvssScore, cvssVector, referenceUrl }) => {\n const cveRepo = new CveRepository(db);\n const cve = cveRepo.create({\n vulnerabilityId,\n cveId,\n description,\n cvssScore,\n cvssVector,\n referenceUrl,\n });\n return { content: [{ type: 'text', text: JSON.stringify(cve, null, 2) }] };\n },\n );\n}\n","/**\n * sonobat — MCP Datalog Tools\n *\n * Tools for querying and analyzing the AttackDataGraph using\n * the Datalog inference engine.\n */\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type Database from 'better-sqlite3';\nimport { z } from 'zod';\nimport { listFacts, runDatalog, queryAttackPaths, listPatterns } from '../../engine/datalog/index.js';\nimport type { Fact, EvalResult } from '../../engine/datalog/types.js';\n\n/**\n * Format a single Fact as Datalog-style text.\n *\n * Example: host(\"abc-123\", \"10.0.0.1\", \"IP\").\n */\nfunction formatFact(fact: Fact): string {\n const args = fact.values\n .map((v) => (typeof v === 'number' ? String(v) : `\"${v}\"`))\n .join(', ');\n return `${fact.predicate}(${args}).`;\n}\n\n/**\n * Format an EvalResult as human-readable text.\n */\nfunction formatEvalResult(result: EvalResult): string {\n if (result.answers.length === 0) {\n return `No query results.\\n\\nStats: ${result.stats.iterations} iterations, ${result.stats.totalDerived} derived facts, ${result.stats.elapsedMs}ms`;\n }\n\n const sections: string[] = [];\n\n for (const answer of result.answers) {\n const queryArgs = answer.query.args\n .map((a) => (a.kind === 'variable' ? a.name : a.kind === 'constant' ? String(a.value) : '_'))\n .join(', ');\n const header = `Query: ${answer.query.predicate}(${queryArgs})`;\n\n if (answer.tuples.length === 0) {\n sections.push(`${header}\\nResults: (empty)`);\n continue;\n }\n\n const columns = answer.columns;\n\n // Calculate column widths\n const widths = columns.map((col, i) =>\n Math.max(\n col.length,\n ...answer.tuples.map((t) => String(t[i]).length),\n ),\n );\n\n // Header row\n const headerRow = columns.map((col, i) => col.padEnd(widths[i])).join(' | ');\n\n // Data rows\n const dataRows = answer.tuples.map(\n (tuple) =>\n ' ' +\n tuple\n .map((val, i) => String(val).padEnd(widths[i]))\n .join(' | '),\n );\n\n sections.push(\n `${header}\\nResults (${answer.tuples.length} rows):\\n ${headerRow}\\n${dataRows.join('\\n')}`,\n );\n }\n\n sections.push(\n `\\nStats: ${result.stats.iterations} iterations, ${result.stats.totalDerived} derived facts, ${result.stats.elapsedMs}ms`,\n );\n\n return sections.join('\\n\\n');\n}\n\n/**\n * Register Datalog-related MCP tools on the server.\n */\nexport function registerDatalogTools(server: McpServer, db: Database.Database): void {\n // 1. list_facts\n server.tool(\n 'list_facts',\n 'List database contents as Datalog facts. Optionally filter by predicate name and limit the number of results.',\n {\n predicate: z\n .string()\n .optional()\n .describe(\n 'Filter by predicate name (host, service, http_endpoint, input, endpoint_input, observation, credential, vulnerability, vulnerability_endpoint, cve, vhost)',\n ),\n limit: z.number().optional().describe('Maximum number of facts to return'),\n },\n async ({ predicate, limit }) => {\n const facts = listFacts(db, predicate, limit);\n if (facts.length === 0) {\n return {\n content: [{ type: 'text', text: 'No facts found.' }],\n };\n }\n const text = facts.map(formatFact).join('\\n');\n return { content: [{ type: 'text', text }] };\n },\n );\n\n // 2. run_datalog\n server.tool(\n 'run_datalog',\n 'Execute a custom Datalog program against the AttackDataGraph. Supports rules with :- and queries with ?-. Optionally save the program as a named rule for future reuse.',\n {\n program: z.string().describe('Datalog program text (rules and queries)'),\n save_name: z.string().optional().describe('Save the program as a named rule for future reuse'),\n save_description: z\n .string()\n .optional()\n .describe('Description of the saved rule'),\n generated_by: z\n .string()\n .optional()\n .describe('Who generated this rule: \"human\" or \"ai\" (default: \"ai\")'),\n },\n async ({ program, save_name, save_description, generated_by }) => {\n try {\n const generatedBy =\n generated_by === 'human' ? 'human' : 'ai';\n const result = runDatalog(db, program, {\n saveName: save_name,\n saveDescription: save_description,\n generatedBy,\n });\n const text = formatEvalResult(result);\n return { content: [{ type: 'text', text }] };\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [{ type: 'text', text: `Datalog error: ${message}` }],\n isError: true,\n };\n }\n },\n );\n\n // 3. query_attack_paths\n server.tool(\n 'query_attack_paths',\n 'Run a preset or saved attack pattern query. Use pattern \"list\" to see all available patterns.',\n {\n pattern: z\n .string()\n .describe(\n 'Pattern name (e.g. \"reachable_services\", \"critical_vulns\") or \"list\" to see available patterns',\n ),\n },\n async ({ pattern }) => {\n if (pattern === 'list') {\n const patterns = listPatterns(db);\n if (patterns.length === 0) {\n return {\n content: [{ type: 'text', text: 'No patterns available.' }],\n };\n }\n const lines = patterns.map(\n (p) =>\n `- ${p.name} [${p.source}]${p.description ? `: ${p.description}` : ''}${p.generatedBy ? ` (by ${p.generatedBy})` : ''}`,\n );\n return {\n content: [\n {\n type: 'text',\n text: `Available patterns:\\n${lines.join('\\n')}`,\n },\n ],\n };\n }\n\n try {\n const result = queryAttackPaths(db, pattern);\n const text = formatEvalResult(result);\n return { content: [{ type: 'text', text }] };\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [{ type: 'text', text: `Datalog error: ${message}` }],\n isError: true,\n };\n }\n },\n );\n}\n","/**\n * sonobat — Datalog engine type definitions\n *\n * AST types, runtime types, configuration, and error types\n * for the Datalog inference engine.\n */\n\n// ============================================================\n// Token types\n// ============================================================\n\nexport type TokenKind =\n | 'IDENT'\n | 'VARIABLE'\n | 'STRING'\n | 'NUMBER'\n | 'LPAREN'\n | 'RPAREN'\n | 'COMMA'\n | 'DOT'\n | 'COLON_DASH'\n | 'NOT'\n | 'NEQ'\n | 'EQ'\n | 'LT'\n | 'GT'\n | 'LTE'\n | 'GTE'\n | 'QUERY'\n | 'UNDERSCORE'\n | 'EOF';\n\nexport interface Token {\n kind: TokenKind;\n value: string;\n line: number;\n col: number;\n}\n\n// ============================================================\n// AST types\n// ============================================================\n\nexport type Term =\n | { kind: 'variable'; name: string }\n | { kind: 'constant'; value: string | number };\n\nexport interface Atom {\n predicate: string;\n args: Term[];\n}\n\nexport type BodyLiteral =\n | { kind: 'positive'; atom: Atom }\n | { kind: 'negated'; atom: Atom }\n | { kind: 'comparison'; op: ComparisonOp; left: Term; right: Term };\n\nexport type ComparisonOp = '=' | '!=' | '<' | '>' | '<=' | '>=';\n\nexport interface Rule {\n head: Atom;\n body: BodyLiteral[];\n}\n\nexport interface Query {\n atom: Atom;\n}\n\nexport interface Program {\n rules: Rule[];\n queries: Query[];\n}\n\n// ============================================================\n// Runtime types\n// ============================================================\n\nexport type Tuple = ReadonlyArray<string | number>;\n\nexport interface EvalConfig {\n /** Maximum fixed-point iterations (default: 1000) */\n maxIterations: number;\n /** Maximum total derived tuples (default: 100_000) */\n maxTuples: number;\n /** Maximum rules allowed (default: 200) */\n maxRules: number;\n /** Timeout in milliseconds (default: 5000) */\n timeoutMs: number;\n}\n\nexport const DEFAULT_EVAL_CONFIG: EvalConfig = {\n maxIterations: 1000,\n maxTuples: 100_000,\n maxRules: 200,\n timeoutMs: 5000,\n};\n\nexport interface QueryAnswer {\n query: Atom;\n tuples: Tuple[];\n columns: string[];\n}\n\nexport interface EvalStats {\n iterations: number;\n totalDerived: number;\n elapsedMs: number;\n}\n\nexport interface EvalResult {\n answers: QueryAnswer[];\n stats: EvalStats;\n}\n\n// ============================================================\n// Fact types (for fact-extractor)\n// ============================================================\n\nexport interface Fact {\n predicate: string;\n values: ReadonlyArray<string | number>;\n}\n\n// ============================================================\n// Rule storage types\n// ============================================================\n\nexport interface DatalogRule {\n id: string;\n name: string;\n description?: string;\n ruleText: string;\n generatedBy: 'human' | 'ai' | 'preset';\n isPreset: boolean;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateDatalogRuleInput {\n name: string;\n description?: string;\n ruleText: string;\n generatedBy: 'human' | 'ai' | 'preset';\n isPreset?: boolean;\n}\n\n// ============================================================\n// Error types\n// ============================================================\n\nexport class DatalogError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'DatalogError';\n }\n}\n\nexport class DatalogSyntaxError extends DatalogError {\n readonly line: number;\n readonly col: number;\n\n constructor(message: string, line: number, col: number) {\n super(`Syntax error at ${line}:${col}: ${message}`);\n this.name = 'DatalogSyntaxError';\n this.line = line;\n this.col = col;\n }\n}\n\nexport class DatalogSafetyError extends DatalogError {\n constructor(message: string) {\n super(message);\n this.name = 'DatalogSafetyError';\n }\n}\n\nexport class DatalogResourceError extends DatalogError {\n constructor(message: string) {\n super(message);\n this.name = 'DatalogResourceError';\n }\n}\n","/**\n * sonobat — Datalog tokenizer\n *\n * Single-pass character scanner that converts Datalog source text\n * into a token array. Tracks line/column numbers and supports % comments.\n */\n\nimport { DatalogSyntaxError } from './types.js';\nimport type { Token } from './types.js';\n\n/**\n * Tokenize a Datalog source string into an array of tokens.\n * The last token is always EOF.\n */\nexport function tokenize(source: string): Token[] {\n const tokens: Token[] = [];\n let pos = 0;\n let line = 1;\n let col = 1;\n\n while (pos < source.length) {\n const ch = source[pos];\n\n // Whitespace\n if (ch === ' ' || ch === '\\t' || ch === '\\r') {\n pos++;\n col++;\n continue;\n }\n\n // Newline\n if (ch === '\\n') {\n pos++;\n line++;\n col = 1;\n continue;\n }\n\n // Comment (% to end of line)\n if (ch === '%') {\n while (pos < source.length && source[pos] !== '\\n') {\n pos++;\n }\n continue;\n }\n\n // String literal\n if (ch === '\"') {\n const startCol = col;\n pos++;\n col++;\n let value = '';\n while (pos < source.length && source[pos] !== '\"') {\n if (source[pos] === '\\n') {\n throw new DatalogSyntaxError('Unterminated string literal', line, startCol);\n }\n if (source[pos] === '\\\\' && pos + 1 < source.length) {\n pos++;\n col++;\n const escaped = source[pos];\n if (escaped === '\"') value += '\"';\n else if (escaped === '\\\\') value += '\\\\';\n else if (escaped === 'n') value += '\\n';\n else if (escaped === 't') value += '\\t';\n else value += escaped;\n } else {\n value += source[pos];\n }\n pos++;\n col++;\n }\n if (pos >= source.length) {\n throw new DatalogSyntaxError('Unterminated string literal', line, startCol);\n }\n pos++; // skip closing quote\n col++;\n tokens.push({ kind: 'STRING', value, line, col: startCol });\n continue;\n }\n\n // Number literal\n if (ch >= '0' && ch <= '9') {\n const startCol = col;\n let value = '';\n while (pos < source.length && source[pos] >= '0' && source[pos] <= '9') {\n value += source[pos];\n pos++;\n col++;\n }\n // Decimal part\n if (pos < source.length && source[pos] === '.' && pos + 1 < source.length && source[pos + 1] >= '0' && source[pos + 1] <= '9') {\n value += '.';\n pos++;\n col++;\n while (pos < source.length && source[pos] >= '0' && source[pos] <= '9') {\n value += source[pos];\n pos++;\n col++;\n }\n }\n tokens.push({ kind: 'NUMBER', value, line, col: startCol });\n continue;\n }\n\n // Identifier, variable, keyword, or underscore\n if (isIdentStart(ch)) {\n const startCol = col;\n let value = '';\n while (pos < source.length && isIdentPart(source[pos])) {\n value += source[pos];\n pos++;\n col++;\n }\n\n // Keywords\n if (value === 'not') {\n tokens.push({ kind: 'NOT', value, line, col: startCol });\n } else if (value === '_') {\n tokens.push({ kind: 'UNDERSCORE', value, line, col: startCol });\n } else if (ch >= 'A' && ch <= 'Z') {\n tokens.push({ kind: 'VARIABLE', value, line, col: startCol });\n } else {\n tokens.push({ kind: 'IDENT', value, line, col: startCol });\n }\n continue;\n }\n\n // Underscore alone\n if (ch === '_') {\n const startCol = col;\n let value = '_';\n pos++;\n col++;\n while (pos < source.length && isIdentPart(source[pos])) {\n value += source[pos];\n pos++;\n col++;\n }\n if (value === '_') {\n tokens.push({ kind: 'UNDERSCORE', value, line, col: startCol });\n } else {\n // _something is treated as a variable\n tokens.push({ kind: 'VARIABLE', value, line, col: startCol });\n }\n continue;\n }\n\n // Single/multi-character symbols\n const startCol = col;\n\n if (ch === '(') { tokens.push({ kind: 'LPAREN', value: '(', line, col: startCol }); pos++; col++; continue; }\n if (ch === ')') { tokens.push({ kind: 'RPAREN', value: ')', line, col: startCol }); pos++; col++; continue; }\n if (ch === ',') { tokens.push({ kind: 'COMMA', value: ',', line, col: startCol }); pos++; col++; continue; }\n if (ch === '.') { tokens.push({ kind: 'DOT', value: '.', line, col: startCol }); pos++; col++; continue; }\n\n // :- (COLON_DASH)\n if (ch === ':' && pos + 1 < source.length && source[pos + 1] === '-') {\n tokens.push({ kind: 'COLON_DASH', value: ':-', line, col: startCol });\n pos += 2;\n col += 2;\n continue;\n }\n\n // ?- (QUERY)\n if (ch === '?' && pos + 1 < source.length && source[pos + 1] === '-') {\n tokens.push({ kind: 'QUERY', value: '?-', line, col: startCol });\n pos += 2;\n col += 2;\n continue;\n }\n\n // Comparison operators\n if (ch === '!' && pos + 1 < source.length && source[pos + 1] === '=') {\n tokens.push({ kind: 'NEQ', value: '!=', line, col: startCol });\n pos += 2;\n col += 2;\n continue;\n }\n if (ch === '<' && pos + 1 < source.length && source[pos + 1] === '=') {\n tokens.push({ kind: 'LTE', value: '<=', line, col: startCol });\n pos += 2;\n col += 2;\n continue;\n }\n if (ch === '>' && pos + 1 < source.length && source[pos + 1] === '=') {\n tokens.push({ kind: 'GTE', value: '>=', line, col: startCol });\n pos += 2;\n col += 2;\n continue;\n }\n if (ch === '<') { tokens.push({ kind: 'LT', value: '<', line, col: startCol }); pos++; col++; continue; }\n if (ch === '>') { tokens.push({ kind: 'GT', value: '>', line, col: startCol }); pos++; col++; continue; }\n if (ch === '=') { tokens.push({ kind: 'EQ', value: '=', line, col: startCol }); pos++; col++; continue; }\n\n throw new DatalogSyntaxError(`Unexpected character '${ch}'`, line, col);\n }\n\n tokens.push({ kind: 'EOF', value: '', line, col });\n return tokens;\n}\n\nfunction isIdentStart(ch: string): boolean {\n return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');\n}\n\nfunction isIdentPart(ch: string): boolean {\n return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch === '_';\n}\n","/**\n * sonobat — Datalog recursive descent parser\n *\n * Converts a Datalog source string into a Program AST.\n * Calls the tokenizer to produce a token stream, then parses\n * rules, facts, and queries using a recursive descent approach.\n *\n * Grammar:\n * program = (rule | query)*\n * rule = atom \":-\" body \".\" | atom \".\"\n * query = \"?-\" atom \".\"\n * body = bodyLiteral (\",\" bodyLiteral)*\n * bodyLiteral = \"not\" atom | comparison | atom\n * comparison = term compOp term\n * atom = IDENT \"(\" termList \")\"\n * termList = term (\",\" term)*\n * term = VARIABLE | STRING | NUMBER | UNDERSCORE\n * compOp = \"=\" | \"!=\" | \"<\" | \">\" | \"<=\" | \">=\"\n */\n\nimport { tokenize } from './tokenizer.js';\nimport {\n DatalogSyntaxError,\n DatalogSafetyError,\n} from './types.js';\nimport type {\n Token,\n TokenKind,\n Program,\n Rule,\n Query,\n Atom,\n BodyLiteral,\n Term,\n ComparisonOp,\n} from './types.js';\n\n/** Comparison operator token kinds */\nconst COMPARISON_OPS: ReadonlySet<TokenKind> = new Set([\n 'EQ',\n 'NEQ',\n 'LT',\n 'GT',\n 'LTE',\n 'GTE',\n]);\n\n/** Map from token kind to ComparisonOp string */\nconst TOKEN_TO_COMP_OP: Readonly<Record<string, ComparisonOp>> = {\n EQ: '=',\n NEQ: '!=',\n LT: '<',\n GT: '>',\n LTE: '<=',\n GTE: '>=',\n};\n\n/**\n * Parse a Datalog source string into a Program AST.\n *\n * @param source - Datalog source code\n * @returns Parsed program containing rules and queries\n * @throws DatalogSyntaxError on parse failure\n * @throws DatalogSafetyError when a rule head contains unsafe variables\n */\nexport function parse(source: string): Program {\n const tokens = tokenize(source);\n const parser = new Parser(tokens);\n return parser.parseProgram();\n}\n\n/**\n * Recursive descent parser for Datalog.\n *\n * Maintains a cursor position into the token stream and provides\n * helper methods for peeking, advancing, and expecting tokens.\n */\nclass Parser {\n private readonly tokens: Token[];\n private pos: number;\n private anonCounter: number;\n\n constructor(tokens: Token[]) {\n this.tokens = tokens;\n this.pos = 0;\n this.anonCounter = 0;\n }\n\n // ===========================================================\n // Token stream helpers\n // ===========================================================\n\n /** Return the current token without advancing. */\n private peek(): Token {\n return this.tokens[this.pos];\n }\n\n /** Advance and return the consumed token. */\n private advance(): Token {\n const token = this.tokens[this.pos];\n this.pos++;\n return token;\n }\n\n /** If the current token matches `kind`, consume and return it; otherwise return null. */\n private match(kind: TokenKind): Token | null {\n if (this.peek().kind === kind) {\n return this.advance();\n }\n return null;\n }\n\n /** Consume the current token if it matches `kind`; throw if it does not. */\n private expect(kind: TokenKind): Token {\n const token = this.peek();\n if (token.kind !== kind) {\n throw new DatalogSyntaxError(\n `Expected ${kind}, got ${token.kind} ('${token.value}')`,\n token.line,\n token.col,\n );\n }\n return this.advance();\n }\n\n /** Generate a unique name for an anonymous variable. */\n private freshAnon(): string {\n const name = `_anon_${this.anonCounter}`;\n this.anonCounter++;\n return name;\n }\n\n // ===========================================================\n // Grammar productions\n // ===========================================================\n\n /** program = (rule | query)* EOF */\n parseProgram(): Program {\n const rules: Rule[] = [];\n const queries: Query[] = [];\n\n while (this.peek().kind !== 'EOF') {\n if (this.peek().kind === 'QUERY') {\n queries.push(this.parseQuery());\n } else {\n rules.push(this.parseRule());\n }\n }\n\n return { rules, queries };\n }\n\n /** query = \"?-\" atom \".\" */\n private parseQuery(): Query {\n this.expect('QUERY');\n const atom = this.parseAtom();\n this.expect('DOT');\n return { atom };\n }\n\n /**\n * rule = atom \":-\" body \".\" | atom \".\"\n *\n * A fact (atom followed by \".\") is represented as a rule with an empty body.\n * After parsing, safety validation is performed for non-fact rules.\n */\n private parseRule(): Rule {\n const head = this.parseAtom();\n\n let body: BodyLiteral[] = [];\n if (this.match('COLON_DASH')) {\n body = this.parseBody();\n }\n\n this.expect('DOT');\n\n const rule: Rule = { head, body };\n this.validateSafety(rule);\n return rule;\n }\n\n /** body = bodyLiteral (\",\" bodyLiteral)* */\n private parseBody(): BodyLiteral[] {\n const literals: BodyLiteral[] = [];\n literals.push(this.parseBodyLiteral());\n\n while (this.match('COMMA')) {\n literals.push(this.parseBodyLiteral());\n }\n\n return literals;\n }\n\n /**\n * bodyLiteral = \"not\" atom | comparison | atom\n *\n * Disambiguation logic:\n * - If the next token is NOT, parse a negated atom.\n * - If the next token is a VARIABLE or constant that could start a comparison\n * (i.e. the token after it is a comparison operator), parse a comparison.\n * - Otherwise, parse a positive atom.\n */\n private parseBodyLiteral(): BodyLiteral {\n // Negation\n if (this.peek().kind === 'NOT') {\n this.advance();\n const atom = this.parseAtom();\n return { kind: 'negated', atom };\n }\n\n // Try to detect comparison: term compOp term\n if (this.isComparisonStart()) {\n return this.parseComparison();\n }\n\n // Positive atom\n const atom = this.parseAtom();\n return { kind: 'positive', atom };\n }\n\n /**\n * Detect whether the current position starts a comparison.\n *\n * A comparison starts with a term (VARIABLE, STRING, NUMBER, UNDERSCORE)\n * followed by a comparison operator. We look ahead to distinguish this\n * from an atom (which starts with IDENT followed by LPAREN).\n */\n private isComparisonStart(): boolean {\n const current = this.peek();\n // A comparison starts with a term, not an IDENT (atoms start with IDENT)\n if (\n current.kind === 'VARIABLE' ||\n current.kind === 'STRING' ||\n current.kind === 'NUMBER' ||\n current.kind === 'UNDERSCORE'\n ) {\n // Check if the next token after the term is a comparison op\n if (this.pos + 1 < this.tokens.length) {\n const next = this.tokens[this.pos + 1];\n return COMPARISON_OPS.has(next.kind);\n }\n }\n return false;\n }\n\n /** comparison = term compOp term */\n private parseComparison(): BodyLiteral {\n const left = this.parseTerm();\n const opToken = this.advance();\n if (!COMPARISON_OPS.has(opToken.kind)) {\n throw new DatalogSyntaxError(\n `Expected comparison operator, got ${opToken.kind}`,\n opToken.line,\n opToken.col,\n );\n }\n const op = TOKEN_TO_COMP_OP[opToken.kind];\n const right = this.parseTerm();\n return { kind: 'comparison', op, left, right };\n }\n\n /** atom = IDENT \"(\" termList \")\" */\n private parseAtom(): Atom {\n const identToken = this.expect('IDENT');\n const predicate = identToken.value;\n\n this.expect('LPAREN');\n const args = this.parseTermList();\n this.expect('RPAREN');\n\n return { predicate, args };\n }\n\n /** termList = term (\",\" term)* */\n private parseTermList(): Term[] {\n const terms: Term[] = [];\n terms.push(this.parseTerm());\n\n while (this.match('COMMA')) {\n terms.push(this.parseTerm());\n }\n\n return terms;\n }\n\n /** term = VARIABLE | STRING | NUMBER | UNDERSCORE */\n private parseTerm(): Term {\n const token = this.peek();\n\n if (token.kind === 'VARIABLE') {\n this.advance();\n return { kind: 'variable', name: token.value };\n }\n\n if (token.kind === 'STRING') {\n this.advance();\n return { kind: 'constant', value: token.value };\n }\n\n if (token.kind === 'NUMBER') {\n this.advance();\n return { kind: 'constant', value: Number(token.value) };\n }\n\n if (token.kind === 'UNDERSCORE') {\n this.advance();\n return { kind: 'variable', name: this.freshAnon() };\n }\n\n throw new DatalogSyntaxError(\n `Expected term (variable, string, number, or _), got ${token.kind} ('${token.value}')`,\n token.line,\n token.col,\n );\n }\n\n // ===========================================================\n // Safety validation\n // ===========================================================\n\n /**\n * Validate that every variable in the rule head appears in at least\n * one positive body literal.\n *\n * Facts (rules with empty body) are exempt: their head may only\n * contain constants, or variables that are trivially safe because\n * there is no body at all. In standard Datalog, facts should only\n * have constants, but we allow variable-free heads to pass.\n *\n * @throws DatalogSafetyError if a head variable is not grounded\n */\n private validateSafety(rule: Rule): void {\n // Facts (empty body) are exempt from safety checks\n if (rule.body.length === 0) {\n return;\n }\n\n // Collect variables from positive body literals\n const positiveVars = new Set<string>();\n for (const literal of rule.body) {\n if (literal.kind === 'positive') {\n for (const arg of literal.atom.args) {\n if (arg.kind === 'variable') {\n positiveVars.add(arg.name);\n }\n }\n }\n }\n\n // Check that every head variable appears in positiveVars\n for (const arg of rule.head.args) {\n if (arg.kind === 'variable') {\n if (!positiveVars.has(arg.name)) {\n throw new DatalogSafetyError(\n `Unsafe variable '${arg.name}' in head of rule '${rule.head.predicate}': ` +\n `it does not appear in any positive body literal`,\n );\n }\n }\n }\n }\n}\n","/**\n * sonobat — Datalog naive bottom-up evaluator\n *\n * Takes a parsed Program AST and base facts, then:\n * 1. Initializes a fact database from base facts and inline facts (empty-body rules)\n * 2. Repeatedly applies rules until a fixed point (no new facts derived)\n * 3. Evaluates queries against the final fact set\n *\n * Uses a naive semi-naive-style evaluation with Map-based fact storage.\n * Supports positive literals, negated literals, and comparison operators.\n */\n\nimport {\n DatalogResourceError,\n DEFAULT_EVAL_CONFIG,\n} from './types.js';\nimport type {\n Program,\n Rule,\n Atom,\n BodyLiteral,\n Term,\n ComparisonOp,\n Fact,\n Tuple,\n EvalConfig,\n EvalResult,\n EvalStats,\n QueryAnswer,\n} from './types.js';\n\n// ============================================================\n// Binding type — maps variable names to concrete values\n// ============================================================\n\ntype Binding = Map<string, string | number>;\n\n// ============================================================\n// FactDB — stores facts indexed by predicate name\n// ============================================================\n\nclass FactDB {\n private readonly store: Map<string, Tuple[]> = new Map();\n private readonly seen: Map<string, Set<string>> = new Map();\n private totalCount = 0;\n\n /** Get all tuples for a predicate. */\n get(predicate: string): ReadonlyArray<Tuple> {\n return this.store.get(predicate) ?? [];\n }\n\n /** Total number of stored tuples across all predicates. */\n get size(): number {\n return this.totalCount;\n }\n\n /**\n * Add a tuple for a predicate. Returns true if it was new.\n * Uses a serialized key for deduplication.\n */\n add(predicate: string, tuple: Tuple): boolean {\n const key = serializeTuple(tuple);\n\n let seenSet = this.seen.get(predicate);\n if (!seenSet) {\n seenSet = new Set();\n this.seen.set(predicate, seenSet);\n }\n\n if (seenSet.has(key)) {\n return false;\n }\n\n seenSet.add(key);\n\n let tuples = this.store.get(predicate);\n if (!tuples) {\n tuples = [];\n this.store.set(predicate, tuples);\n }\n tuples.push(tuple);\n this.totalCount++;\n return true;\n }\n}\n\n/** Serialize a tuple into a unique string key for deduplication. */\nfunction serializeTuple(tuple: Tuple): string {\n return tuple\n .map((v) => (typeof v === 'number' ? `n:${v}` : `s:${v}`))\n .join('\\0');\n}\n\n// ============================================================\n// Core evaluation\n// ============================================================\n\n/**\n * Evaluate a Datalog program with base facts.\n *\n * @param program - Parsed Datalog program (rules + queries)\n * @param baseFacts - Base facts from the database\n * @param config - Optional resource limits\n * @returns Evaluation result with query answers and statistics\n * @throws DatalogResourceError when resource limits are exceeded\n */\nexport function evaluate(\n program: Program,\n baseFacts: Fact[],\n config?: Partial<EvalConfig>,\n): EvalResult {\n const cfg: EvalConfig = { ...DEFAULT_EVAL_CONFIG, ...config };\n const startTime = performance.now();\n\n // Separate facts (empty-body rules) from real rules\n const inlineFacts: Rule[] = [];\n const realRules: Rule[] = [];\n for (const rule of program.rules) {\n if (rule.body.length === 0) {\n inlineFacts.push(rule);\n } else {\n realRules.push(rule);\n }\n }\n\n // Check maxRules limit\n if (realRules.length > cfg.maxRules) {\n throw new DatalogResourceError(\n `Number of rules (${realRules.length}) exceeds maxRules limit (${cfg.maxRules})`,\n );\n }\n\n // Initialize fact database\n const db = new FactDB();\n\n // Load base facts\n for (const fact of baseFacts) {\n db.add(fact.predicate, fact.values);\n }\n\n // Load inline facts (rules with empty body)\n for (const rule of inlineFacts) {\n const tuple = rule.head.args.map((arg) => {\n if (arg.kind === 'constant') {\n return arg.value;\n }\n // Variables in facts without body are not meaningful,\n // but we allow them as-is (parser already validates safety)\n return arg.name;\n });\n db.add(rule.head.predicate, tuple);\n }\n\n // Fixed-point evaluation\n const stats: EvalStats = { iterations: 0, totalDerived: 0, elapsedMs: 0 };\n\n if (realRules.length > 0) {\n let changed = true;\n while (changed) {\n // Check timeout\n const elapsed = performance.now() - startTime;\n if (elapsed > cfg.timeoutMs) {\n throw new DatalogResourceError(\n `Evaluation timeout: exceeded ${cfg.timeoutMs}ms`,\n );\n }\n\n // Check iteration limit\n if (stats.iterations >= cfg.maxIterations) {\n throw new DatalogResourceError(\n `Iteration limit exceeded: ${cfg.maxIterations}`,\n );\n }\n\n changed = false;\n stats.iterations++;\n\n for (const rule of realRules) {\n const derivedTuples = evaluateRule(rule, db);\n for (const tuple of derivedTuples) {\n // Check tuple limit\n if (db.size >= cfg.maxTuples) {\n throw new DatalogResourceError(\n `Tuple limit exceeded: ${cfg.maxTuples}`,\n );\n }\n const isNew = db.add(rule.head.predicate, tuple);\n if (isNew) {\n changed = true;\n stats.totalDerived++;\n }\n }\n }\n }\n }\n\n stats.elapsedMs = performance.now() - startTime;\n\n // Evaluate queries\n const answers: QueryAnswer[] = program.queries.map((q) =>\n evaluateQuery(q.atom, db),\n );\n\n return { answers, stats };\n}\n\n// ============================================================\n// Rule evaluation — derive new tuples from a single rule\n// ============================================================\n\n/**\n * Evaluate a single rule against the fact database.\n * Returns all new tuples derivable by the rule's head.\n */\nfunction evaluateRule(rule: Rule, db: FactDB): Tuple[] {\n const bindings = evaluateBody(rule.body, 0, new Map(), db);\n const result: Tuple[] = [];\n const seen = new Set<string>();\n\n for (const binding of bindings) {\n const tuple = instantiateHead(rule.head, binding);\n const key = serializeTuple(tuple);\n if (!seen.has(key)) {\n seen.add(key);\n result.push(tuple);\n }\n }\n\n return result;\n}\n\n/**\n * Recursively evaluate body literals against the database.\n * Returns all valid bindings that satisfy all body literals.\n *\n * @param body - Array of body literals to evaluate\n * @param index - Current literal index being processed\n * @param binding - Current variable bindings\n * @param db - Fact database\n * @returns Array of complete bindings satisfying all literals\n */\nfunction evaluateBody(\n body: BodyLiteral[],\n index: number,\n binding: Binding,\n db: FactDB,\n): Binding[] {\n // Base case: all literals satisfied\n if (index >= body.length) {\n return [binding];\n }\n\n const literal = body[index];\n const results: Binding[] = [];\n\n if (literal.kind === 'positive') {\n // Try unifying the atom against all matching facts\n const facts = db.get(literal.atom.predicate);\n for (const factTuple of facts) {\n const newBinding = unifyAtom(literal.atom, factTuple, binding);\n if (newBinding !== null) {\n const subResults = evaluateBody(body, index + 1, newBinding, db);\n for (const r of subResults) {\n results.push(r);\n }\n }\n }\n } else if (literal.kind === 'negated') {\n // Negation: succeeds if NO facts match under current binding\n const facts = db.get(literal.atom.predicate);\n let anyMatch = false;\n for (const factTuple of facts) {\n const newBinding = unifyAtom(literal.atom, factTuple, binding);\n if (newBinding !== null) {\n anyMatch = true;\n break;\n }\n }\n if (!anyMatch) {\n // Negation succeeds — continue with current binding\n const subResults = evaluateBody(body, index + 1, binding, db);\n for (const r of subResults) {\n results.push(r);\n }\n }\n } else if (literal.kind === 'comparison') {\n // Comparison: evaluate against bound values\n if (evaluateComparison(literal.op, literal.left, literal.right, binding)) {\n const subResults = evaluateBody(body, index + 1, binding, db);\n for (const r of subResults) {\n results.push(r);\n }\n }\n }\n\n return results;\n}\n\n// ============================================================\n// Unification\n// ============================================================\n\n/**\n * Attempt to unify an atom's arguments with a fact tuple,\n * extending the given binding. Returns a new binding on success,\n * or null if unification fails.\n */\nfunction unifyAtom(\n atom: Atom,\n factTuple: Tuple,\n binding: Binding,\n): Binding | null {\n // Arity mismatch\n if (atom.args.length !== factTuple.length) {\n return null;\n }\n\n // Work on a copy so we don't mutate the input\n let current = new Map(binding);\n\n for (let i = 0; i < atom.args.length; i++) {\n const term = atom.args[i];\n const value = factTuple[i];\n\n const result = unifyTerm(term, value, current);\n if (result === null) {\n return null;\n }\n current = result;\n }\n\n return current;\n}\n\n/**\n * Unify a single term against a concrete value.\n * Returns updated binding on success, null on failure.\n */\nfunction unifyTerm(\n term: Term,\n value: string | number,\n binding: Binding,\n): Binding | null {\n if (term.kind === 'constant') {\n // Constant must match exactly\n return term.value === value ? binding : null;\n }\n\n // Variable — check if already bound\n const existing = binding.get(term.name);\n if (existing !== undefined) {\n // Must match the existing binding\n return existing === value ? binding : null;\n }\n\n // Bind the variable\n const newBinding = new Map(binding);\n newBinding.set(term.name, value);\n return newBinding;\n}\n\n// ============================================================\n// Comparison evaluation\n// ============================================================\n\n/**\n * Evaluate a comparison operator against bound term values.\n * Returns false if any term is unbound (which shouldn't happen\n * in safe Datalog, but we handle it defensively).\n */\nfunction evaluateComparison(\n op: ComparisonOp,\n left: Term,\n right: Term,\n binding: Binding,\n): boolean {\n const leftVal = resolveTerm(left, binding);\n const rightVal = resolveTerm(right, binding);\n\n if (leftVal === null || rightVal === null) {\n return false;\n }\n\n switch (op) {\n case '=':\n return leftVal === rightVal;\n case '!=':\n return leftVal !== rightVal;\n case '<':\n return leftVal < rightVal;\n case '>':\n return leftVal > rightVal;\n case '<=':\n return leftVal <= rightVal;\n case '>=':\n return leftVal >= rightVal;\n }\n}\n\n/**\n * Resolve a term to its concrete value using the current binding.\n * Returns null if a variable is unbound.\n */\nfunction resolveTerm(\n term: Term,\n binding: Binding,\n): string | number | null {\n if (term.kind === 'constant') {\n return term.value;\n }\n const val = binding.get(term.name);\n return val !== undefined ? val : null;\n}\n\n// ============================================================\n// Head instantiation\n// ============================================================\n\n/**\n * Instantiate a rule head with the given binding,\n * producing a concrete tuple.\n */\nfunction instantiateHead(head: Atom, binding: Binding): Tuple {\n return head.args.map((term) => {\n if (term.kind === 'constant') {\n return term.value;\n }\n const val = binding.get(term.name);\n if (val === undefined) {\n // This shouldn't happen in a safe Datalog program\n return term.name;\n }\n return val;\n });\n}\n\n// ============================================================\n// Query evaluation\n// ============================================================\n\n/**\n * Evaluate a query atom against the final fact database.\n * Returns matching tuples and the column names (variable names in the query).\n */\nfunction evaluateQuery(atom: Atom, db: FactDB): QueryAnswer {\n // Extract column names (variable names in query)\n const columns: string[] = [];\n for (const arg of atom.args) {\n if (arg.kind === 'variable') {\n columns.push(arg.name);\n }\n }\n\n // Find matching tuples\n const facts = db.get(atom.predicate);\n const tuples: Tuple[] = [];\n const seen = new Set<string>();\n\n for (const factTuple of facts) {\n const binding = unifyAtom(atom, factTuple, new Map());\n if (binding !== null) {\n // For the result, return the full fact tuple (not just bound vars)\n const key = serializeTuple(factTuple);\n if (!seen.has(key)) {\n seen.add(key);\n tuples.push(factTuple);\n }\n }\n }\n\n return {\n query: atom,\n tuples,\n columns,\n };\n}\n","/**\n * sonobat — Datalog fact extractor\n *\n * Extracts Fact[] from the SQLite database for use by the Datalog evaluator.\n * Each database table maps to one or more fact predicates.\n */\n\nimport type Database from 'better-sqlite3';\nimport type { Fact } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Row types for direct SQL queries\n// ---------------------------------------------------------------------------\n\ninterface HostRow {\n id: string;\n authority: string;\n authority_kind: string;\n}\n\ninterface ServiceRow {\n host_id: string;\n id: string;\n transport: string;\n port: number;\n app_proto: string;\n state: string;\n}\n\ninterface HttpEndpointRow {\n service_id: string;\n id: string;\n method: string;\n path: string;\n status_code: number | null;\n}\n\ninterface InputRow {\n service_id: string;\n id: string;\n location: string;\n name: string;\n}\n\ninterface EndpointInputRow {\n endpoint_id: string;\n input_id: string;\n}\n\ninterface ObservationRow {\n input_id: string;\n id: string;\n raw_value: string;\n source: string;\n confidence: string;\n}\n\ninterface CredentialRow {\n service_id: string;\n id: string;\n username: string;\n secret_type: string;\n source: string;\n confidence: string;\n}\n\ninterface VulnerabilityRow {\n service_id: string;\n id: string;\n vuln_type: string;\n title: string;\n severity: string;\n confidence: string;\n endpoint_id: string | null;\n}\n\ninterface CveRow {\n vulnerability_id: string;\n cve_id: string;\n cvss_score: number | null;\n}\n\ninterface VhostRow {\n host_id: string;\n id: string;\n hostname: string;\n source: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Predicate extraction functions\n// ---------------------------------------------------------------------------\n\ntype ExtractorFn = (db: Database.Database, limit?: number) => Fact[];\n\nfunction extractHosts(db: Database.Database, limit?: number): Fact[] {\n const sql = limit !== undefined\n ? 'SELECT id, authority, authority_kind FROM hosts LIMIT ?'\n : 'SELECT id, authority, authority_kind FROM hosts';\n const rows = limit !== undefined\n ? db.prepare<[number], HostRow>(sql).all(limit)\n : db.prepare<[], HostRow>(sql).all();\n return rows.map((r) => ({\n predicate: 'host',\n values: [r.id, r.authority, r.authority_kind],\n }));\n}\n\nfunction extractServices(db: Database.Database, limit?: number): Fact[] {\n const sql = limit !== undefined\n ? 'SELECT host_id, id, transport, port, app_proto, state FROM services LIMIT ?'\n : 'SELECT host_id, id, transport, port, app_proto, state FROM services';\n const rows = limit !== undefined\n ? db.prepare<[number], ServiceRow>(sql).all(limit)\n : db.prepare<[], ServiceRow>(sql).all();\n return rows.map((r) => ({\n predicate: 'service',\n values: [r.host_id, r.id, r.transport, r.port, r.app_proto, r.state],\n }));\n}\n\nfunction extractHttpEndpoints(db: Database.Database, limit?: number): Fact[] {\n const sql = limit !== undefined\n ? 'SELECT service_id, id, method, path, status_code FROM http_endpoints LIMIT ?'\n : 'SELECT service_id, id, method, path, status_code FROM http_endpoints';\n const rows = limit !== undefined\n ? db.prepare<[number], HttpEndpointRow>(sql).all(limit)\n : db.prepare<[], HttpEndpointRow>(sql).all();\n return rows.map((r) => ({\n predicate: 'http_endpoint',\n values: [r.service_id, r.id, r.method, r.path, r.status_code ?? 0],\n }));\n}\n\nfunction extractInputs(db: Database.Database, limit?: number): Fact[] {\n const sql = limit !== undefined\n ? 'SELECT service_id, id, location, name FROM inputs LIMIT ?'\n : 'SELECT service_id, id, location, name FROM inputs';\n const rows = limit !== undefined\n ? db.prepare<[number], InputRow>(sql).all(limit)\n : db.prepare<[], InputRow>(sql).all();\n return rows.map((r) => ({\n predicate: 'input',\n values: [r.service_id, r.id, r.location, r.name],\n }));\n}\n\nfunction extractEndpointInputs(db: Database.Database, limit?: number): Fact[] {\n const sql = limit !== undefined\n ? 'SELECT endpoint_id, input_id FROM endpoint_inputs LIMIT ?'\n : 'SELECT endpoint_id, input_id FROM endpoint_inputs';\n const rows = limit !== undefined\n ? db.prepare<[number], EndpointInputRow>(sql).all(limit)\n : db.prepare<[], EndpointInputRow>(sql).all();\n return rows.map((r) => ({\n predicate: 'endpoint_input',\n values: [r.endpoint_id, r.input_id],\n }));\n}\n\nfunction extractObservations(db: Database.Database, limit?: number): Fact[] {\n const sql = limit !== undefined\n ? 'SELECT input_id, id, raw_value, source, confidence FROM observations LIMIT ?'\n : 'SELECT input_id, id, raw_value, source, confidence FROM observations';\n const rows = limit !== undefined\n ? db.prepare<[number], ObservationRow>(sql).all(limit)\n : db.prepare<[], ObservationRow>(sql).all();\n return rows.map((r) => ({\n predicate: 'observation',\n values: [r.input_id, r.id, r.raw_value, r.source, r.confidence],\n }));\n}\n\nfunction extractCredentials(db: Database.Database, limit?: number): Fact[] {\n const sql = limit !== undefined\n ? 'SELECT service_id, id, username, secret_type, source, confidence FROM credentials LIMIT ?'\n : 'SELECT service_id, id, username, secret_type, source, confidence FROM credentials';\n const rows = limit !== undefined\n ? db.prepare<[number], CredentialRow>(sql).all(limit)\n : db.prepare<[], CredentialRow>(sql).all();\n return rows.map((r) => ({\n predicate: 'credential',\n values: [r.service_id, r.id, r.username, r.secret_type, r.source, r.confidence],\n }));\n}\n\nfunction extractVulnerabilities(db: Database.Database, limit?: number): Fact[] {\n const sql = limit !== undefined\n ? 'SELECT service_id, id, vuln_type, title, severity, confidence, endpoint_id FROM vulnerabilities LIMIT ?'\n : 'SELECT service_id, id, vuln_type, title, severity, confidence, endpoint_id FROM vulnerabilities';\n const rows = limit !== undefined\n ? db.prepare<[number], VulnerabilityRow>(sql).all(limit)\n : db.prepare<[], VulnerabilityRow>(sql).all();\n\n const facts: Fact[] = [];\n for (const r of rows) {\n facts.push({\n predicate: 'vulnerability',\n values: [r.service_id, r.id, r.vuln_type, r.title, r.severity, r.confidence],\n });\n }\n return facts;\n}\n\nfunction extractVulnerabilityEndpoints(db: Database.Database, limit?: number): Fact[] {\n const sql = limit !== undefined\n ? 'SELECT id, endpoint_id FROM vulnerabilities WHERE endpoint_id IS NOT NULL LIMIT ?'\n : 'SELECT id, endpoint_id FROM vulnerabilities WHERE endpoint_id IS NOT NULL';\n const rows = limit !== undefined\n ? db.prepare<[number], { id: string; endpoint_id: string }>(sql).all(limit)\n : db.prepare<[], { id: string; endpoint_id: string }>(sql).all();\n return rows.map((r) => ({\n predicate: 'vulnerability_endpoint',\n values: [r.id, r.endpoint_id],\n }));\n}\n\nfunction extractCves(db: Database.Database, limit?: number): Fact[] {\n const sql = limit !== undefined\n ? 'SELECT vulnerability_id, cve_id, cvss_score FROM cves LIMIT ?'\n : 'SELECT vulnerability_id, cve_id, cvss_score FROM cves';\n const rows = limit !== undefined\n ? db.prepare<[number], CveRow>(sql).all(limit)\n : db.prepare<[], CveRow>(sql).all();\n return rows.map((r) => ({\n predicate: 'cve',\n values: [r.vulnerability_id, r.cve_id, r.cvss_score ?? 0],\n }));\n}\n\nfunction extractVhosts(db: Database.Database, limit?: number): Fact[] {\n const sql = limit !== undefined\n ? 'SELECT host_id, id, hostname, source FROM vhosts LIMIT ?'\n : 'SELECT host_id, id, hostname, source FROM vhosts';\n const rows = limit !== undefined\n ? db.prepare<[number], VhostRow>(sql).all(limit)\n : db.prepare<[], VhostRow>(sql).all();\n return rows.map((r) => ({\n predicate: 'vhost',\n values: [r.host_id, r.id, r.hostname, r.source ?? ''],\n }));\n}\n\n// ---------------------------------------------------------------------------\n// Predicate → extractor mapping\n// ---------------------------------------------------------------------------\n\nconst EXTRACTORS: ReadonlyMap<string, ExtractorFn> = new Map<string, ExtractorFn>([\n ['host', extractHosts],\n ['service', extractServices],\n ['http_endpoint', extractHttpEndpoints],\n ['input', extractInputs],\n ['endpoint_input', extractEndpointInputs],\n ['observation', extractObservations],\n ['credential', extractCredentials],\n ['vulnerability', extractVulnerabilities],\n ['vulnerability_endpoint', extractVulnerabilityEndpoints],\n ['cve', extractCves],\n ['vhost', extractVhosts],\n]);\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Extract all facts from the SQLite database.\n *\n * Iterates over every supported predicate and extracts facts\n * from the corresponding tables.\n */\nexport function extractFacts(db: Database.Database): Fact[] {\n const facts: Fact[] = [];\n for (const extractor of EXTRACTORS.values()) {\n facts.push(...extractor(db));\n }\n return facts;\n}\n\n/**\n * Extract facts for a specific predicate only.\n *\n * Used by the `list_facts` MCP tool to retrieve facts\n * for a single predicate with optional limit.\n *\n * @param db - SQLite database instance\n * @param predicate - The fact predicate name (e.g. 'host', 'service')\n * @param limit - Optional maximum number of facts to return\n * @returns Array of facts matching the predicate\n */\nexport function extractFactsByPredicate(\n db: Database.Database,\n predicate: string,\n limit?: number,\n): Fact[] {\n const extractor = EXTRACTORS.get(predicate);\n if (!extractor) {\n return [];\n }\n return extractor(db, limit);\n}\n","/**\n * sonobat — Datalog preset rules\n *\n * Predefined Datalog rule patterns for common penetration testing\n * analysis queries. These rules can be loaded by name and executed\n * against the fact base extracted from the database.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface PresetRule {\n name: string;\n description: string;\n ruleText: string;\n}\n\n// ---------------------------------------------------------------------------\n// Preset rule definitions\n// ---------------------------------------------------------------------------\n\nconst PRESET_RULES: readonly PresetRule[] = [\n {\n name: 'reachable_services',\n description: 'Find all open services on each host with their port and application protocol.',\n ruleText: [\n 'reachable(Host, Port, AppProto) :- service(Host, _, _, Port, AppProto, \"open\").',\n '?- reachable(Host, Port, AppProto).',\n ].join('\\n'),\n },\n {\n name: 'authenticated_access',\n description: 'Services with discovered credentials, showing host, port, and username.',\n ruleText: [\n 'auth_access(Host, Port, Username) :- service(Host, Svc, _, Port, _, \"open\"), credential(Svc, _, Username, _, _, _).',\n '?- auth_access(Host, Port, Username).',\n ].join('\\n'),\n },\n {\n name: 'exploitable_endpoints',\n description: 'HTTP endpoints with known vulnerabilities, including vulnerability type and severity.',\n ruleText: [\n 'exploitable(Host, Port, Path, VulnType, Severity) :- service(Host, Svc, _, Port, _, _), http_endpoint(Svc, Ep, _, Path, _), vulnerability_endpoint(Vuln, Ep), vulnerability(Svc, Vuln, VulnType, _, Severity, _).',\n '?- exploitable(Host, Port, Path, VulnType, Severity).',\n ].join('\\n'),\n },\n {\n name: 'critical_vulns',\n description: 'Critical severity vulnerabilities with host, port, title, and type.',\n ruleText: [\n 'critical(Host, Port, Title, VulnType) :- service(Host, Svc, _, Port, _, _), vulnerability(Svc, _, VulnType, Title, \"critical\", _).',\n '?- critical(Host, Port, Title, VulnType).',\n ].join('\\n'),\n },\n {\n name: 'attack_surface',\n description: 'Full attack surface overview combining host, port, endpoint path, input name, and input location.',\n ruleText: [\n 'surface(Host, Port, Path, InputName, Location) :- service(Host, Svc, _, Port, _, \"open\"), http_endpoint(Svc, Ep, _, Path, _), endpoint_input(Ep, Inp), input(Svc, Inp, Location, InputName).',\n '?- surface(Host, Port, Path, InputName, Location).',\n ].join('\\n'),\n },\n {\n name: 'unfuzzed_inputs',\n description: 'Inputs with observations but no associated vulnerability endpoint — candidates for fuzzing.',\n ruleText: [\n 'unfuzzed(Host, Port, Path, InputName) :- service(Host, Svc, _, Port, _, \"open\"), http_endpoint(Svc, Ep, _, Path, _), endpoint_input(Ep, Inp), input(Svc, Inp, _, InputName), observation(Inp, _, _, _, _), not vulnerability_endpoint(_, Ep).',\n '?- unfuzzed(Host, Port, Path, InputName).',\n ].join('\\n'),\n },\n] as const;\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Get all preset Datalog rules.\n *\n * @returns Array of all preset rules\n */\nexport function getPresetRules(): PresetRule[] {\n return [...PRESET_RULES];\n}\n\n/**\n * Get a specific preset rule by name.\n *\n * @param name - The preset rule name (e.g. 'reachable_services')\n * @returns The preset rule if found, undefined otherwise\n */\nexport function getPresetRule(name: string): PresetRule | undefined {\n return PRESET_RULES.find((r) => r.name === name);\n}\n","import type Database from 'better-sqlite3';\nimport crypto from 'node:crypto';\nimport type { DatalogRule, CreateDatalogRuleInput } from '../../engine/datalog/types.js';\n\n/**\n * Raw row shape returned by better-sqlite3 for the `datalog_rules` table.\n */\ninterface DatalogRuleRow {\n id: string;\n name: string;\n description: string | null;\n rule_text: string;\n generated_by: string;\n is_preset: number;\n created_at: string;\n updated_at: string;\n}\n\n/** Maps a snake_case DB row to a camelCase DatalogRule entity. */\nfunction rowToDatalogRule(row: DatalogRuleRow): DatalogRule {\n return {\n id: row.id,\n name: row.name,\n description: row.description ?? undefined,\n ruleText: row.rule_text,\n generatedBy: row.generated_by as DatalogRule['generatedBy'],\n isPreset: row.is_preset === 1,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\n/**\n * Repository for the `datalog_rules` table.\n */\nexport class DatalogRuleRepository {\n private readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n }\n\n /** Insert a new DatalogRule and return the full entity. */\n create(input: CreateDatalogRuleInput): DatalogRule {\n const id = crypto.randomUUID();\n const now = new Date().toISOString();\n\n const stmt = this.db.prepare<\n [string, string, string | null, string, string, number, string, string]\n >(\n `INSERT INTO datalog_rules (id, name, description, rule_text, generated_by, is_preset, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n\n stmt.run(\n id,\n input.name,\n input.description ?? null,\n input.ruleText,\n input.generatedBy,\n input.isPreset ? 1 : 0,\n now,\n now,\n );\n\n return {\n id,\n name: input.name,\n description: input.description,\n ruleText: input.ruleText,\n generatedBy: input.generatedBy,\n isPreset: input.isPreset ?? false,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n /** Find a DatalogRule by its primary key. */\n findById(id: string): DatalogRule | undefined {\n const stmt = this.db.prepare<[string], DatalogRuleRow>(\n `SELECT id, name, description, rule_text, generated_by, is_preset, created_at, updated_at\n FROM datalog_rules WHERE id = ?`,\n );\n const row = stmt.get(id);\n return row ? rowToDatalogRule(row) : undefined;\n }\n\n /** Find a DatalogRule by name. */\n findByName(name: string): DatalogRule | undefined {\n const stmt = this.db.prepare<[string], DatalogRuleRow>(\n `SELECT id, name, description, rule_text, generated_by, is_preset, created_at, updated_at\n FROM datalog_rules WHERE name = ?`,\n );\n const row = stmt.get(name);\n return row ? rowToDatalogRule(row) : undefined;\n }\n\n /** Return all DatalogRules. */\n findAll(): DatalogRule[] {\n const stmt = this.db.prepare<[], DatalogRuleRow>(\n `SELECT id, name, description, rule_text, generated_by, is_preset, created_at, updated_at\n FROM datalog_rules ORDER BY created_at`,\n );\n return stmt.all().map(rowToDatalogRule);\n }\n\n /** Delete a DatalogRule by id. Returns true if a row was deleted. */\n delete(id: string): boolean {\n const stmt = this.db.prepare<[string]>('DELETE FROM datalog_rules WHERE id = ?');\n const result = stmt.run(id);\n return result.changes > 0;\n }\n}\n","/**\n * sonobat — Datalog inference engine public API\n *\n * Provides three main functions:\n * - listFacts: Show database contents as Datalog facts\n * - runDatalog: Execute a custom Datalog program against the database\n * - queryAttackPaths: Run a preset attack pattern analysis\n */\n\nimport type Database from 'better-sqlite3';\nimport type { EvalConfig, EvalResult, Fact } from './types.js';\nimport { parse } from './parser.js';\nimport { evaluate } from './evaluator.js';\nimport { extractFacts, extractFactsByPredicate } from './fact-extractor.js';\nimport { getPresetRule, getPresetRules } from './preset-rules.js';\nimport type { PresetRule } from './preset-rules.js';\nimport { DatalogRuleRepository } from '../../db/repository/datalog-rule-repository.js';\n\nexport type { PresetRule };\n\n/**\n * List facts from the database, optionally filtered by predicate.\n */\nexport function listFacts(\n db: Database.Database,\n predicate?: string,\n limit?: number,\n): Fact[] {\n if (predicate !== undefined) {\n return extractFactsByPredicate(db, predicate, limit);\n }\n const facts = extractFacts(db);\n if (limit !== undefined && limit > 0) {\n return facts.slice(0, limit);\n }\n return facts;\n}\n\n/**\n * Run a custom Datalog program against the database.\n * Optionally save the program as a named rule for future reuse.\n */\nexport function runDatalog(\n db: Database.Database,\n program: string,\n options?: {\n config?: Partial<EvalConfig>;\n saveName?: string;\n saveDescription?: string;\n generatedBy?: 'human' | 'ai';\n },\n): EvalResult {\n const ast = parse(program);\n const facts = extractFacts(db);\n const result = evaluate(ast, facts, options?.config);\n\n // Optionally save the rule\n if (options?.saveName !== undefined) {\n const ruleRepo = new DatalogRuleRepository(db);\n ruleRepo.create({\n name: options.saveName,\n description: options.saveDescription,\n ruleText: program,\n generatedBy: options.generatedBy ?? 'ai',\n });\n }\n\n return result;\n}\n\n/**\n * Run a preset or saved attack pattern query.\n */\nexport function queryAttackPaths(\n db: Database.Database,\n pattern: string,\n config?: Partial<EvalConfig>,\n): EvalResult {\n // Try preset rules first\n const preset = getPresetRule(pattern);\n if (preset !== undefined) {\n const ast = parse(preset.ruleText);\n const facts = extractFacts(db);\n return evaluate(ast, facts, config);\n }\n\n // Then try saved rules from DB\n const ruleRepo = new DatalogRuleRepository(db);\n const savedRule = ruleRepo.findByName(pattern);\n if (savedRule !== undefined) {\n const ast = parse(savedRule.ruleText);\n const facts = extractFacts(db);\n return evaluate(ast, facts, config);\n }\n\n // Return empty result if pattern not found\n return {\n answers: [],\n stats: { iterations: 0, totalDerived: 0, elapsedMs: 0 },\n };\n}\n\n/**\n * List all available patterns (presets + saved rules).\n */\nexport function listPatterns(db: Database.Database): Array<{\n name: string;\n description?: string;\n source: 'preset' | 'saved';\n generatedBy?: string;\n}> {\n const presets = getPresetRules().map((p) => ({\n name: p.name,\n description: p.description,\n source: 'preset' as const,\n }));\n\n const ruleRepo = new DatalogRuleRepository(db);\n const saved = ruleRepo.findAll().map((r) => ({\n name: r.name,\n description: r.description,\n source: 'saved' as const,\n generatedBy: r.generatedBy,\n }));\n\n return [...presets, ...saved];\n}\n","/**\n * sonobat — MCP Resources\n *\n * Read-only resources for browsing the AttackDataGraph.\n */\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type Database from 'better-sqlite3';\nimport { HostRepository } from '../db/repository/host-repository.js';\nimport { ServiceRepository } from '../db/repository/service-repository.js';\nimport { VhostRepository } from '../db/repository/vhost-repository.js';\nimport { HttpEndpointRepository } from '../db/repository/http-endpoint-repository.js';\nimport { InputRepository } from '../db/repository/input-repository.js';\nimport { VulnerabilityRepository } from '../db/repository/vulnerability-repository.js';\n\nexport function registerResources(server: McpServer, db: Database.Database): void {\n const hostRepo = new HostRepository(db);\n const serviceRepo = new ServiceRepository(db);\n const vhostRepo = new VhostRepository(db);\n const httpEndpointRepo = new HttpEndpointRepository(db);\n const inputRepo = new InputRepository(db);\n const vulnRepo = new VulnerabilityRepository(db);\n\n // 1. sonobat://hosts — Host list\n server.resource('hosts', 'sonobat://hosts', { description: 'List of all discovered hosts' }, async () => {\n const hosts = hostRepo.findAll();\n return {\n contents: [\n {\n uri: 'sonobat://hosts',\n mimeType: 'application/json',\n text: JSON.stringify(hosts, null, 2),\n },\n ],\n };\n });\n\n // 2. sonobat://hosts/{id} — Host detail tree\n server.resource(\n 'host-detail',\n 'sonobat://hosts/{id}',\n { description: 'Detailed host tree with services, endpoints, inputs, and vulnerabilities' },\n async (uri) => {\n // Extract host ID from the URI\n const hostId = uri.pathname.split('/').pop() ?? '';\n const host = hostRepo.findById(hostId);\n if (!host) {\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: 'application/json',\n text: JSON.stringify({ error: `Host not found: ${hostId}` }),\n },\n ],\n };\n }\n\n const services = serviceRepo.findByHostId(hostId);\n const vhosts = vhostRepo.findByHostId(hostId);\n\n const serviceTree = services.map((service) => {\n const endpoints = httpEndpointRepo.findByServiceId(service.id);\n const inputs = inputRepo.findByServiceId(service.id);\n const vulnerabilities = vulnRepo.findByServiceId(service.id);\n return { ...service, endpoints, inputs, vulnerabilities };\n });\n\n const result = { ...host, services: serviceTree, vhosts };\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: 'application/json',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n },\n );\n\n // 3. sonobat://summary — Statistics summary\n server.resource(\n 'summary',\n 'sonobat://summary',\n { description: 'Summary statistics of the AttackDataGraph' },\n async () => {\n // Use direct SQL COUNT queries for efficiency\n const counts: Record<string, number> = {};\n const tables = [\n 'hosts',\n 'services',\n 'http_endpoints',\n 'inputs',\n 'observations',\n 'credentials',\n 'vulnerabilities',\n 'cves',\n 'vhosts',\n 'artifacts',\n ];\n\n for (const table of tables) {\n const row = db.prepare(`SELECT COUNT(*) as count FROM ${table}`).get() as { count: number };\n counts[table] = row.count;\n }\n\n return {\n contents: [\n {\n uri: 'sonobat://summary',\n mimeType: 'application/json',\n text: JSON.stringify(counts, null, 2),\n },\n ],\n };\n },\n );\n}\n"],"mappings":";;;AAOA,OAAO,cAAc;AACrB,SAAS,4BAA4B;;;ACD9B,IAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["db","db","crypto","db","crypto","db","crypto","db","crypto","db","stmt","rows","crypto","db","crypto","db","crypto","db","stmt","rows","server","db","z","crypto","crypto","db","transport","isRecord","crypto","db","crypto","db","crypto","db","db","db","crypto","server","db","z","path","z","db","server","db","z","z","db","server","z","z","startCol","atom","db","db","crypto","db","db","server","db","z","server","db","db","server"]}