File size: 4,669 Bytes
20f108c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Phantom β€” Session persistence + knowledge graph
// Saves/restores minimal state across restarts.
// Linked knowledge: tool ↔ book/CVE cross-reference.

import fs from "fs";
import { resolve } from "path";
import { MEMORY_DIR } from "./config.mjs";

const SESSION_FILE = resolve(MEMORY_DIR, "session.json");
const GRAPH_FILE = resolve(MEMORY_DIR, "graph.json");

// ── Session ──
export function saveSession(data) {
  try {
    const dir = MEMORY_DIR;
    if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
    const existing = loadSession();
    const merged = { ...existing, ...data, savedAt: Date.now() };
    fs.writeFileSync(SESSION_FILE, JSON.stringify(merged, null, 2), "utf-8");
    return true;
  } catch { return false; }
}

export function loadSession() {
  try {
    if (!fs.existsSync(SESSION_FILE)) return {};
    return JSON.parse(fs.readFileSync(SESSION_FILE, "utf-8"));
  } catch { return {}; }
}

export function clearSession() {
  try { if (fs.existsSync(SESSION_FILE)) fs.unlinkSync(SESSION_FILE); } catch {}
}

// ── Knowledge Graph ──
// Links tools to related books/CVEs/techniques
// Format: { "tool_name": { books: ["book1", "book2"], cves: ["CVE-2025-..."], tags: ["xss", "sqli"] } }

export function loadGraph() {
  try {
    if (!fs.existsSync(GRAPH_FILE)) return {};
    return JSON.parse(fs.readFileSync(GRAPH_FILE, "utf-8"));
  } catch { return {}; }
}

export function saveGraph(graph) {
  try {
    const dir = MEMORY_DIR;
    if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
    fs.writeFileSync(GRAPH_FILE, JSON.stringify(graph, null, 2), "utf-8");
    return true;
  } catch { return false; }
}

export function linkTool(toolName, { books = [], cves = [], tags = [] } = {}) {
  const graph = loadGraph();
  if (!graph[toolName]) graph[toolName] = { books: [], cves: [], tags: [] };
  if (books.length) graph[toolName].books = [...new Set([...graph[toolName].books, ...books])];
  if (cves.length) graph[toolName].cves = [...new Set([...graph[toolName].cves, ...cves])];
  if (tags.length) graph[toolName].tags = [...new Set([...graph[toolName].tags, ...tags])];
  saveGraph(graph);
  return graph[toolName];
}

export function queryGraph(query) {
  const graph = loadGraph();
  const q = query.toLowerCase();
  const results = [];
  for (const [tool, info] of Object.entries(graph)) {
    let score = 0;
    if (tool.includes(q)) score += 10;
    if (info.tags?.some(t => t.includes(q))) score += 5;
    if (info.cves?.some(c => c.toLowerCase().includes(q))) score += 3;
    if (info.books?.some(b => b.toLowerCase().includes(q))) score += 2;
    // Partial match
    const words = q.split(/\s+/);
    for (const w of words) {
      if (w.length < 2) continue;
      if (tool.includes(w)) score += 3;
      if (info.tags?.some(t => t.includes(w))) score += 2;
      if (info.books?.some(b => b.toLowerCase().includes(w))) score += 1;
    }
    if (score > 0) results.push({ tool, ...info, score });
  }
  return results.sort((a, b) => (b.score || 0) - (a.score || 0));
}

// ── Auto-link from books directory ──
// Scans books/ for filenames matching tool categories
export function autoLinkFromBooks() {
  const booksDir = resolve(MEMORY_DIR, "../books");
  if (!fs.existsSync(booksDir)) return 0;
  const files = fs.readdirSync(booksDir).filter(f => f.endsWith(".txt"));
  const graph = loadGraph();
  let linked = 0;
  for (const file of files) {
    const name = file.replace(".txt", "").toLowerCase();
    // Map book names to tool tags
    const tagMap = {
      sql_injection: ["sql_detect", "sqlmap", "sqli", "sql_injection"],
      xss: ["xss_scan", "xss"],
      ssrf: ["ssrf", "ssrf_test"],
      nmap: ["nmap", "port_scan", "network_scan"],
      cve: ["cve_search", "searchsploit", "vuln_scan", "vulners"],
      pentest: ["nuclei", "metasploit", "exploit", "pentest"],
      owasp: ["wafw00f", "whatweb", "nikto", "dirb"],
      methodology: ["recon", "enumeration", "osint"],
      shannon: ["gap_analysis", "self_improve", "evolve"],
      auto: ["self_improve", "evolve", "code_gen"],
      tool: ["workflow", "automation", "batch"],
      reference: ["hash", "encode", "decode", "crypto"],
    };
    for (const [tag, tools] of Object.entries(tagMap)) {
      if (name.includes(tag) || tag.includes(name)) {
        for (const t of tools) {
          if (!graph[t]) graph[t] = { books: [], cves: [], tags: [] };
          if (!graph[t].books.includes(file)) { graph[t].books.push(file); linked++; }
          if (!graph[t].tags.includes(tag)) { graph[t].tags.push(tag); }
        }
      }
    }
  }
  if (linked > 0) saveGraph(graph);
  return linked;
}