k8s-av 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/cli/docker.d.ts +0 -16
  2. package/dist/cli/docker.js +3 -42
  3. package/dist/cli/index.d.ts +0 -14
  4. package/dist/cli/index.js +0 -36
  5. package/dist/cli/scan.d.ts +0 -8
  6. package/dist/cli/scan.js +0 -17
  7. package/dist/cli/start.d.ts +0 -14
  8. package/dist/cli/start.js +2 -40
  9. package/dist/core/attack-path.d.ts +0 -11
  10. package/dist/core/attack-path.js +0 -38
  11. package/dist/core/cve-enricher.d.ts +0 -7
  12. package/dist/core/cve-enricher.js +1 -38
  13. package/dist/core/fetcher.d.ts +0 -8
  14. package/dist/core/fetcher.js +2 -29
  15. package/dist/core/schema.d.ts +0 -30
  16. package/dist/core/schema.js +0 -35
  17. package/dist/core/transformer.d.ts +0 -15
  18. package/dist/core/transformer.js +0 -85
  19. package/dist/db/loader.d.ts +0 -12
  20. package/dist/db/loader.js +0 -75
  21. package/dist/db/neo4j-client.d.ts +0 -27
  22. package/dist/db/neo4j-client.js +1 -58
  23. package/dist/db/queries.d.ts +0 -47
  24. package/dist/db/queries.js +0 -63
  25. package/dist/db/test.d.ts +0 -17
  26. package/dist/db/test.js +1 -41
  27. package/dist/db/types.d.ts +0 -24
  28. package/dist/db/types.js +0 -14
  29. package/dist/schemas/index.js +0 -10
  30. package/dist/server/routes/blast.js +0 -10
  31. package/dist/server/routes/critical.js +0 -21
  32. package/dist/server/routes/cycles.js +0 -10
  33. package/dist/server/routes/graph.js +0 -6
  34. package/dist/server/routes/ingest.js +0 -14
  35. package/dist/server/routes/paths.js +0 -15
  36. package/dist/server/routes/report.js +0 -15
  37. package/dist/server/routes/simulate.js +0 -15
  38. package/dist/server/routes/vulnerabilities.js +0 -18
  39. package/dist/server/server.d.ts +0 -13
  40. package/dist/server/server.js +0 -36
  41. package/dist/services/ingestion.service.d.ts +0 -11
  42. package/dist/services/ingestion.service.js +0 -21
  43. package/dist/services/report/formatter.d.ts +0 -9
  44. package/dist/services/report/formatter.js +0 -25
  45. package/dist/services/report/generator.d.ts +0 -11
  46. package/dist/services/report/generator.js +0 -23
  47. package/dist/services/risk-explainer.d.ts +0 -29
  48. package/dist/services/risk-explainer.js +0 -53
  49. package/package.json +49 -46
  50. package/readme.md +284 -0
  51. package/ui/src/components/Sidebar.tsx +8 -8
  52. package/ui/src/lib/api.ts +0 -6
  53. package/ui/src/store/useAppStore.ts +0 -7
  54. package/ui/src/views/CriticalNodeView.tsx +29 -23
  55. package/ui/src/views/PathsView.tsx +29 -18
  56. package/ui/src/views/VulnerabilitiesView.tsx +12 -12
@@ -1,9 +1,3 @@
1
- /**
2
- * docker.ts — Docker detection and Neo4j container lifecycle helpers.
3
- *
4
- * Used by the `start` and `ingest` CLI commands before they touch Neo4j.
5
- * The `scan` command is Docker-free and never calls this module.
6
- */
7
1
  declare const NEO4J_BOLT_PORT = 7687;
8
2
  declare const NEO4J_HTTP_PORT = 7474;
9
3
  export declare function checkDockerInstalled(): Promise<boolean>;
@@ -14,16 +8,6 @@ export declare function waitForNeo4j(timeoutMs?: number, pollMs?: number): Promi
14
8
  export interface PreflightResult {
15
9
  ok: boolean;
16
10
  }
17
- /**
18
- * Run the full Docker preflight:
19
- * 1. Docker installed?
20
- * 2. Docker daemon running?
21
- * 3. Neo4j container up? (start it if not)
22
- * 4. Neo4j accepting connections?
23
- *
24
- * Prints clear, actionable messages for every failure case.
25
- * Never throws — returns { ok: false } so the caller can exit cleanly.
26
- */
27
11
  export declare function runPreflight(): Promise<PreflightResult>;
28
12
  export { NEO4J_BOLT_PORT, NEO4J_HTTP_PORT };
29
13
  //# sourceMappingURL=docker.d.ts.map
@@ -1,10 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * docker.ts — Docker detection and Neo4j container lifecycle helpers.
4
- *
5
- * Used by the `start` and `ingest` CLI commands before they touch Neo4j.
6
- * The `scan` command is Docker-free and never calls this module.
7
- */
8
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
3
  if (k2 === undefined) k2 = k;
10
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -50,22 +44,17 @@ const child_process_1 = require("child_process");
50
44
  const path = __importStar(require("path"));
51
45
  const util = __importStar(require("util"));
52
46
  const exec = util.promisify(child_process_1.exec);
53
- // ─── Constants ───────────────────────────────────────────────────────────────
54
47
  const CONTAINER_NAME = 'k8s-attack-neo4j';
55
48
  const COMPOSE_DIR = path.resolve(__dirname, '..', '..', 'docker');
56
49
  const NEO4J_BOLT_PORT = 7687;
57
50
  exports.NEO4J_BOLT_PORT = NEO4J_BOLT_PORT;
58
51
  const NEO4J_HTTP_PORT = 7474;
59
52
  exports.NEO4J_HTTP_PORT = NEO4J_HTTP_PORT;
60
- // ─── Logging helpers ─────────────────────────────────────────────────────────
61
53
  const ok = (msg) => console.log(` ✔ ${msg}`);
62
54
  const warn = (msg) => console.log(` ⚠ ${msg}`);
63
55
  const fail = (msg) => console.error(` ✖ ${msg}`);
64
56
  const info = (msg) => console.log(` ${msg}`);
65
57
  const line = () => console.log(' ' + '─'.repeat(58));
66
- // ─────────────────────────────────────────────────────────────────────────────
67
- // PART 1 — Is Docker installed?
68
- // ─────────────────────────────────────────────────────────────────────────────
69
58
  async function checkDockerInstalled() {
70
59
  try {
71
60
  const { stdout } = await exec('docker --version');
@@ -77,12 +66,9 @@ async function checkDockerInstalled() {
77
66
  return false;
78
67
  }
79
68
  }
80
- // ─────────────────────────────────────────────────────────────────────────────
81
- // PART 2 — Is the Docker daemon running?
82
- // ─────────────────────────────────────────────────────────────────────────────
83
69
  async function checkDockerRunning() {
84
70
  try {
85
- await exec('docker ps');
71
+ await exec('docker ps', { timeout: 8000 });
86
72
  ok('Docker daemon is running');
87
73
  return true;
88
74
  }
@@ -90,9 +76,6 @@ async function checkDockerRunning() {
90
76
  return false;
91
77
  }
92
78
  }
93
- // ─────────────────────────────────────────────────────────────────────────────
94
- // PART 3 — Is the Neo4j container already up?
95
- // ─────────────────────────────────────────────────────────────────────────────
96
79
  async function isNeo4jContainerRunning() {
97
80
  try {
98
81
  const { stdout } = await exec(`docker inspect --format "{{.State.Running}}" ${CONTAINER_NAME}`);
@@ -102,9 +85,6 @@ async function isNeo4jContainerRunning() {
102
85
  return false;
103
86
  }
104
87
  }
105
- // ─────────────────────────────────────────────────────────────────────────────
106
- // PART 4 — Start Neo4j via docker-compose
107
- // ─────────────────────────────────────────────────────────────────────────────
108
88
  async function startNeo4j() {
109
89
  return new Promise((resolve, reject) => {
110
90
  const child = (0, child_process_1.spawn)('docker', ['compose', 'up', '-d', '--remove-orphans'], { cwd: COMPOSE_DIR, stdio: 'pipe', shell: process.platform === 'win32' });
@@ -121,18 +101,14 @@ async function startNeo4j() {
121
101
  child.once('error', reject);
122
102
  });
123
103
  }
124
- // ─────────────────────────────────────────────────────────────────────────────
125
- // PART 5 — Poll until Neo4j Bolt port is accepting connections
126
- // ─────────────────────────────────────────────────────────────────────────────
127
104
  async function waitForNeo4j(timeoutMs = 120000, pollMs = 3000) {
128
105
  const deadline = Date.now() + timeoutMs;
129
106
  let attempt = 0;
130
107
  while (Date.now() < deadline) {
131
108
  attempt++;
132
109
  try {
133
- // Use `docker exec` to run a lightweight cypher-shell ping
134
110
  await exec(`docker exec ${CONTAINER_NAME} cypher-shell -u neo4j -p password "RETURN 1" --format plain`);
135
- return; // success
111
+ return;
136
112
  }
137
113
  catch {
138
114
  const remaining = Math.ceil((deadline - Date.now()) / 1000);
@@ -144,21 +120,10 @@ async function waitForNeo4j(timeoutMs = 120000, pollMs = 3000) {
144
120
  throw new Error(`Neo4j did not become ready within ${timeoutMs / 1000}s.\n` +
145
121
  ` Check container logs: docker logs ${CONTAINER_NAME}`);
146
122
  }
147
- /**
148
- * Run the full Docker preflight:
149
- * 1. Docker installed?
150
- * 2. Docker daemon running?
151
- * 3. Neo4j container up? (start it if not)
152
- * 4. Neo4j accepting connections?
153
- *
154
- * Prints clear, actionable messages for every failure case.
155
- * Never throws — returns { ok: false } so the caller can exit cleanly.
156
- */
157
123
  async function runPreflight() {
158
124
  console.log('\n' + '─'.repeat(62));
159
125
  console.log(' Docker & Neo4j Preflight');
160
126
  console.log('─'.repeat(62));
161
- // ── 1. Docker installed ───────────────────────────────────────────────────
162
127
  const installed = await checkDockerInstalled();
163
128
  if (!installed) {
164
129
  fail('Docker is not installed.\n');
@@ -175,7 +140,6 @@ async function runPreflight() {
175
140
  line();
176
141
  return { ok: false };
177
142
  }
178
- // ── 2. Docker daemon running ──────────────────────────────────────────────
179
143
  const running = await checkDockerRunning();
180
144
  if (!running) {
181
145
  warn('Docker is installed but the daemon is not running.\n');
@@ -189,7 +153,6 @@ async function runPreflight() {
189
153
  line();
190
154
  return { ok: false };
191
155
  }
192
- // ── 3. Neo4j container ───────────────────────────────────────────────────
193
156
  const neo4jUp = await isNeo4jContainerRunning();
194
157
  if (neo4jUp) {
195
158
  ok(`Neo4j container running (${CONTAINER_NAME})`);
@@ -214,11 +177,10 @@ async function runPreflight() {
214
177
  return { ok: false };
215
178
  }
216
179
  }
217
- // ── 4. Wait for Neo4j to be ready ────────────────────────────────────────
218
180
  info(`Neo4j ports: Bolt :${NEO4J_BOLT_PORT} Browser :${NEO4J_HTTP_PORT}`);
219
181
  try {
220
182
  await waitForNeo4j(120000);
221
- process.stdout.write('\n'); // clear the spinner line
183
+ process.stdout.write('\n');
222
184
  ok('Neo4j is ready');
223
185
  }
224
186
  catch (err) {
@@ -234,7 +196,6 @@ async function runPreflight() {
234
196
  line();
235
197
  return { ok: true };
236
198
  }
237
- // ─── Utility ─────────────────────────────────────────────────────────────────
238
199
  function sleep(ms) {
239
200
  return new Promise((r) => setTimeout(r, ms));
240
201
  }
@@ -1,16 +1,2 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Kubernetes Attack Path Visualizer — CLI Entry Point
4
- *
5
- * Commands:
6
- * scan — fetch + transform + enrich → write cluster-graph.json (no Neo4j)
7
- * ingest — full pipeline: scan → load Neo4j → re-project GDS
8
- * report — generate + print attack report from Neo4j
9
- *
10
- * Usage:
11
- * npx ts-node src/cli/index.ts scan --mock
12
- * npx ts-node src/cli/index.ts ingest --source mock
13
- * npx ts-node src/cli/index.ts report --format text
14
- */
15
1
  import 'dotenv/config';
16
2
  //# sourceMappingURL=index.d.ts.map
package/dist/cli/index.js CHANGED
@@ -1,18 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- /**
4
- * Kubernetes Attack Path Visualizer — CLI Entry Point
5
- *
6
- * Commands:
7
- * scan — fetch + transform + enrich → write cluster-graph.json (no Neo4j)
8
- * ingest — full pipeline: scan → load Neo4j → re-project GDS
9
- * report — generate + print attack report from Neo4j
10
- *
11
- * Usage:
12
- * npx ts-node src/cli/index.ts scan --mock
13
- * npx ts-node src/cli/index.ts ingest --source mock
14
- * npx ts-node src/cli/index.ts report --format text
15
- */
16
3
  Object.defineProperty(exports, "__esModule", { value: true });
17
4
  require("dotenv/config");
18
5
  const commander_1 = require("commander");
@@ -32,9 +19,6 @@ program
32
19
  'Ingests cluster data, builds an RBAC graph, enriches with CVEs,\n' +
33
20
  'detects attack paths from entry points to crown jewels.')
34
21
  .version('1.0.0', '-v, --version');
35
- // ─────────────────────────────────────────────────────────────────────────────
36
- // scan — local pipeline only (no Neo4j)
37
- // ─────────────────────────────────────────────────────────────────────────────
38
22
  program
39
23
  .command('scan')
40
24
  .description('Scan a Kubernetes cluster and output an attack-path graph (no Neo4j)')
@@ -52,10 +36,6 @@ program
52
36
  process.exit(1);
53
37
  }
54
38
  });
55
- // ─────────────────────────────────────────────────────────────────────────────
56
- // ingest — full pipeline: scan → Neo4j → GDS
57
- // Reuses same services as POST /api/ingest
58
- // ─────────────────────────────────────────────────────────────────────────────
59
39
  program
60
40
  .command('ingest')
61
41
  .description('Full ingestion: fetch cluster data → load Neo4j → re-project GDS')
@@ -68,22 +48,17 @@ program
68
48
  console.log('\n' + '═'.repeat(60));
69
49
  console.log(' 🔷 K8s Attack Path Visualizer — Ingest');
70
50
  console.log('═'.repeat(60));
71
- // ── Docker + Neo4j preflight ──────────────────────────────────────────
72
51
  const preflight = await (0, docker_1.runPreflight)();
73
52
  if (!preflight.ok)
74
53
  process.exit(1);
75
- // Verify Neo4j driver connection
76
54
  console.log('\n Connecting to Neo4j...');
77
55
  await (0, neo4j_client_1.verifyConnection)();
78
- // ── Step 1: ingestCluster (Teammate 1) ───────────────────────────────
79
56
  console.log('\n [1/3] Ingesting cluster data...');
80
57
  const ingestResult = await (0, ingestion_service_1.ingestCluster)({ source, skipCve: opts.skipCve });
81
58
  console.log(` ✔ Graph JSON written: ${ingestResult.nodes} nodes, ${ingestResult.edges} edges`);
82
- // ── Step 2: loadGraph (Teammate 2) ───────────────────────────────────
83
59
  console.log('\n [2/3] Loading graph into Neo4j...');
84
60
  const stats = await (0, loader_1.loadGraph)(ingestResult.graphPath, opts.wipe);
85
61
  console.log(` ✔ Neo4j: ${stats.nodesLoaded} nodes, ${stats.edgesLoaded} edges (${stats.durationMs}ms)`);
86
- // ── Step 3: Re-project GDS ────────────────────────────────────────────
87
62
  console.log('\n [3/3] Projecting GDS graph...');
88
63
  await (0, queries_1.ensureProjection)(true);
89
64
  console.log(' ✔ GDS projection ready');
@@ -96,10 +71,6 @@ program
96
71
  process.exit(1);
97
72
  }
98
73
  });
99
- // ─────────────────────────────────────────────────────────────────────────────
100
- // report — generate + print attack report
101
- // Reuses same generator + formatter as GET /api/report
102
- // ─────────────────────────────────────────────────────────────────────────────
103
74
  program
104
75
  .command('report')
105
76
  .description('Generate a full attack-path report from Neo4j data')
@@ -111,7 +82,6 @@ program
111
82
  await (0, neo4j_client_1.verifyConnection)();
112
83
  console.log(' Generating report...\n');
113
84
  const data = await (0, generator_1.generateReport)();
114
- // Same formatter used by the API — no duplication
115
85
  const output = (0, formatter_1.formatReport)(data, format);
116
86
  console.log(output);
117
87
  process.exit(0);
@@ -121,9 +91,6 @@ program
121
91
  process.exit(1);
122
92
  }
123
93
  });
124
- // ─────────────────────────────────────────────────────────────────────────────
125
- // start — full system orchestrator: backend + ingest + UI + browser
126
- // ─────────────────────────────────────────────────────────────────────────────
127
94
  program
128
95
  .command('start')
129
96
  .description('Start backend, load mock data, open the UI in your browser')
@@ -141,9 +108,6 @@ program
141
108
  process.exit(1);
142
109
  }
143
110
  });
144
- // ─────────────────────────────────────────────────────────────────────────────
145
- // Fallback
146
- // ─────────────────────────────────────────────────────────────────────────────
147
111
  program.on('command:*', () => {
148
112
  console.error(`Unknown command: ${program.args.join(' ')}`);
149
113
  program.help();
@@ -1,16 +1,8 @@
1
1
  export interface ScanOptions {
2
- /** Load data from mock JSON instead of running kubectl */
3
2
  mock: boolean;
4
- /** Destination file path for the cluster-graph.json output */
5
3
  output: string;
6
- /** Skip CVE enrichment (faster runs, no network required) */
7
4
  skipCve?: boolean;
8
- /** Print verbose attack-path report including alternate routes */
9
5
  verbose?: boolean;
10
6
  }
11
- /**
12
- * Orchestrates the full ingestion → transformation → enrichment →
13
- * validation → output pipeline.
14
- */
15
7
  export declare function runScan(options: ScanOptions): Promise<void>;
16
8
  //# sourceMappingURL=scan.d.ts.map
package/dist/cli/scan.js CHANGED
@@ -41,28 +41,17 @@ const transformer_1 = require("../core/transformer");
41
41
  const cve_enricher_1 = require("../core/cve-enricher");
42
42
  const schema_1 = require("../core/schema");
43
43
  const attack_path_1 = require("../core/attack-path");
44
- // ─────────────────────────────────────────────────────────────────────────────
45
- // LOGGING HELPERS
46
- // ─────────────────────────────────────────────────────────────────────────────
47
44
  function step(label) {
48
45
  console.log(`\n✔ ${label}`);
49
46
  }
50
47
  function divider() {
51
48
  console.log(' ' + '─'.repeat(60));
52
49
  }
53
- // ─────────────────────────────────────────────────────────────────────────────
54
- // MAIN SCAN PIPELINE
55
- // ─────────────────────────────────────────────────────────────────────────────
56
- /**
57
- * Orchestrates the full ingestion → transformation → enrichment →
58
- * validation → output pipeline.
59
- */
60
50
  async function runScan(options) {
61
51
  console.log('\n' + '═'.repeat(62));
62
52
  console.log(' 🔐 Kubernetes Attack Path Visualizer');
63
53
  console.log(' RBAC Graph Ingestion & CVE Enrichment Pipeline');
64
54
  console.log('═'.repeat(62));
65
- // ── Step 1: Fetch ──────────────────────────────────────────────────────────
66
55
  step('Fetching cluster data...');
67
56
  const rawData = await (0, fetcher_1.fetchClusterData)(options.mock);
68
57
  const podCount = (rawData.pods?.items ?? []).length;
@@ -73,14 +62,12 @@ async function runScan(options) {
73
62
  (rawData.clusterRoleBindings?.items ?? []).length;
74
63
  console.log(` → Pods: ${podCount} | ServiceAccounts: ${saCount} | ` +
75
64
  `Roles: ${roleCount} | Bindings: ${bindingCount}`);
76
- // ── Step 2: Transform ─────────────────────────────────────────────────────
77
65
  step('Transforming RBAC graph...');
78
66
  let graph = (0, transformer_1.transformToGraph)(rawData);
79
67
  console.log(` → Built ${graph.nodes.length} nodes and ${graph.edges.length} edges`);
80
68
  const entryPts = graph.nodes.filter((n) => n.isEntryPoint).length;
81
69
  const crownJs = graph.nodes.filter((n) => n.isCrownJewel).length;
82
70
  console.log(` → Entry points: ${entryPts} | Crown jewels: ${crownJs}`);
83
- // ── Step 3: CVE Enrichment ────────────────────────────────────────────────
84
71
  if (options.skipCve) {
85
72
  console.log('\n✔ CVE enrichment skipped (--skip-cve)');
86
73
  }
@@ -90,7 +77,6 @@ async function runScan(options) {
90
77
  const enriched = graph.nodes.filter((n) => (n.cve?.length ?? 0) > 0).length;
91
78
  console.log(` → ${enriched} pod(s) enriched with CVE data`);
92
79
  }
93
- // ── Step 4: Attack Path Detection ─────────────────────────────────────────
94
80
  step('Detecting attack paths...');
95
81
  let attackPaths;
96
82
  if (options.verbose) {
@@ -112,7 +98,6 @@ async function runScan(options) {
112
98
  }
113
99
  }
114
100
  graph = { ...graph, attackPaths };
115
- // ── Step 5: Validation ────────────────────────────────────────────────────
116
101
  step('Validating schema...');
117
102
  const finalGraph = {
118
103
  ...graph,
@@ -127,7 +112,6 @@ async function runScan(options) {
127
112
  },
128
113
  };
129
114
  const validated = (0, schema_1.validateGraph)(finalGraph);
130
- // ── Step 6: Persist ───────────────────────────────────────────────────────
131
115
  step('Saving graph...');
132
116
  const outputPath = path.resolve(options.output);
133
117
  const outputDir = path.dirname(outputPath);
@@ -136,7 +120,6 @@ async function runScan(options) {
136
120
  }
137
121
  fs.writeFileSync(outputPath, JSON.stringify(validated, null, 2), 'utf8');
138
122
  console.log(` → Saved to: ${outputPath}`);
139
- // ── Final Summary ─────────────────────────────────────────────────────────
140
123
  divider();
141
124
  console.log('\n 📊 Final Summary\n');
142
125
  console.log(` Nodes : ${validated.nodes.length}`);
@@ -1,17 +1,3 @@
1
- /**
2
- * start.ts — CLI orchestrator
3
- *
4
- * Sequence:
5
- * 0. Docker + Neo4j preflight (skipped with --mock)
6
- * 1. Spawn Express backend (ts-node or node dist/server/server.js)
7
- * 2. Poll /health until ready (60s timeout)
8
- * 3. POST /api/ingest (load cluster data)
9
- * 4. Ensure UI deps installed (npm install inside ui/ if needed)
10
- * 5. Spawn Vite frontend (npm run dev inside ui/)
11
- * 6. Wait for Vite (poll localhost:5173, 30s timeout)
12
- * 7. Open browser
13
- * 8. Park — Ctrl+C kills both children cleanly
14
- */
15
1
  export interface StartOptions {
16
2
  skipBrowser: boolean;
17
3
  source: 'mock' | 'live';
package/dist/cli/start.js CHANGED
@@ -1,18 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * start.ts — CLI orchestrator
4
- *
5
- * Sequence:
6
- * 0. Docker + Neo4j preflight (skipped with --mock)
7
- * 1. Spawn Express backend (ts-node or node dist/server/server.js)
8
- * 2. Poll /health until ready (60s timeout)
9
- * 3. POST /api/ingest (load cluster data)
10
- * 4. Ensure UI deps installed (npm install inside ui/ if needed)
11
- * 5. Spawn Vite frontend (npm run dev inside ui/)
12
- * 6. Wait for Vite (poll localhost:5173, 30s timeout)
13
- * 7. Open browser
14
- * 8. Park — Ctrl+C kills both children cleanly
15
- */
16
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
3
  if (k2 === undefined) k2 = k;
18
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -55,17 +41,12 @@ const http = __importStar(require("http"));
55
41
  const util = __importStar(require("util"));
56
42
  const docker_1 = require("./docker");
57
43
  const exec = util.promisify(child_process_1.exec);
58
- // ─── Resolved paths ───────────────────────────────────────────────────────────
59
- // __dirname is dist/cli/ at runtime (both ts-node and compiled).
60
- // ROOT is always the package root (two levels up from dist/cli/).
61
44
  const ROOT = path.resolve(__dirname, '..', '..');
62
45
  const UI_DIR = path.join(ROOT, 'ui');
63
46
  const BACKEND_URL = 'http://localhost:3001';
64
47
  const FRONTEND_URL = 'http://localhost:5173';
65
- // ─── Helpers ──────────────────────────────────────────────────────────────────
66
48
  const log = (msg) => console.log(` ${msg}`);
67
49
  const warn = (msg) => console.log(` ⚠ ${msg}`);
68
- /** Poll a URL until it responds < 400. Rejects after timeoutMs. */
69
50
  function waitFor(url, timeoutMs = 30000) {
70
51
  return new Promise((resolve, reject) => {
71
52
  const deadline = Date.now() + timeoutMs;
@@ -89,7 +70,6 @@ function waitFor(url, timeoutMs = 30000) {
89
70
  attempt();
90
71
  });
91
72
  }
92
- /** POST /api/ingest — load cluster data into Neo4j */
93
73
  async function ingest(source) {
94
74
  const res = await fetch(`${BACKEND_URL}/api/ingest`, {
95
75
  method: 'POST',
@@ -101,18 +81,12 @@ async function ingest(source) {
101
81
  throw new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`);
102
82
  }
103
83
  }
104
- /** Open the system default browser cross-platform */
105
84
  function openBrowser(url) {
106
85
  const [cmd, args] = process.platform === 'win32' ? ['cmd', ['/c', `start ${url}`]] :
107
86
  process.platform === 'darwin' ? ['open', [url]] :
108
87
  ['xdg-open', [url]];
109
88
  (0, child_process_1.spawn)(cmd, args, { shell: false, detached: true, stdio: 'ignore' }).unref();
110
89
  }
111
- /**
112
- * Ensure the ui/ directory has its node_modules installed.
113
- * Runs `npm install --omit=dev` inside ui/ if node_modules is absent.
114
- * This handles the `npx k8s-av` case where ui/node_modules is not present.
115
- */
116
90
  async function ensureUiDeps() {
117
91
  const nmDir = path.join(UI_DIR, 'node_modules');
118
92
  if (fs.existsSync(nmDir))
@@ -133,7 +107,6 @@ async function runStart(opts) {
133
107
  console.log(' 🔐 Kubernetes Attack Path Visualizer');
134
108
  console.log(' ' + (opts.source === 'mock' ? 'Demo mode (mock data)' : 'Live cluster mode'));
135
109
  console.log('═'.repeat(62));
136
- // ── 0. Docker preflight (skipped in mock mode) ────────────────────────────
137
110
  if (opts.source === 'mock') {
138
111
  console.log('\n ℹ Mock mode — skipping Docker & Neo4j preflight.');
139
112
  }
@@ -143,9 +116,7 @@ async function runStart(opts) {
143
116
  process.exit(1);
144
117
  }
145
118
  console.log();
146
- // ── 1. Spawn backend ───────────────────────────────────────────────────────
147
119
  log('✔ Starting backend...');
148
- // Use compiled JS if available (npm package context), fall back to ts-node
149
120
  const distServer = path.join(ROOT, 'dist', 'server', 'server.js');
150
121
  const [backendCmd, backendArgs] = fs.existsSync(distServer)
151
122
  ? ['node', [distServer]]
@@ -153,7 +124,7 @@ async function runStart(opts) {
153
124
  const backend = (0, child_process_1.spawn)(backendCmd, backendArgs, {
154
125
  cwd: ROOT,
155
126
  stdio: ['ignore', 'pipe', 'pipe'],
156
- shell: process.platform === 'win32',
127
+ shell: false,
157
128
  env: { ...process.env, CORS_ORIGIN: FRONTEND_URL },
158
129
  });
159
130
  let backendExited = false;
@@ -174,7 +145,6 @@ async function runStart(opts) {
174
145
  process.exit(1);
175
146
  }
176
147
  });
177
- // ── 2. Wait for backend ────────────────────────────────────────────────────
178
148
  log('⏳ Waiting for backend...');
179
149
  try {
180
150
  await waitFor(`${BACKEND_URL}/health`, 60000);
@@ -189,7 +159,6 @@ async function runStart(opts) {
189
159
  process.exit(1);
190
160
  }
191
161
  log('✔ Backend ready');
192
- // ── 3. Ingest data ────────────────────────────────────────────────────────
193
162
  log(`✔ Loading cluster data (source: ${opts.source})...`);
194
163
  try {
195
164
  await ingest(opts.source);
@@ -199,7 +168,6 @@ async function runStart(opts) {
199
168
  warn(`Ingest warning: ${err.message}`);
200
169
  warn(' UI will start in empty state — check Neo4j connection.');
201
170
  }
202
- // ── 4. Ensure UI dependencies ─────────────────────────────────────────────
203
171
  try {
204
172
  await ensureUiDeps();
205
173
  }
@@ -208,12 +176,11 @@ async function runStart(opts) {
208
176
  backend.kill();
209
177
  process.exit(1);
210
178
  }
211
- // ── 5. Spawn frontend ─────────────────────────────────────────────────────
212
179
  log('✔ Starting UI...');
213
180
  const frontend = (0, child_process_1.spawn)('npm', ['run', 'dev'], {
214
181
  cwd: UI_DIR,
215
182
  stdio: ['ignore', 'pipe', 'pipe'],
216
- shell: process.platform === 'win32',
183
+ shell: true,
217
184
  env: process.env,
218
185
  });
219
186
  frontend.stdout?.on('data', (d) => {
@@ -226,26 +193,22 @@ async function runStart(opts) {
226
193
  if (line)
227
194
  process.stderr.write(` [ui] ${line}\n`);
228
195
  });
229
- // ── 6. Wait for Vite ──────────────────────────────────────────────────────
230
196
  try {
231
197
  await waitFor(FRONTEND_URL, 45000);
232
198
  }
233
199
  catch {
234
200
  warn('Vite did not respond in time — opening browser anyway.');
235
201
  }
236
- // ── 7. Open browser ───────────────────────────────────────────────────────
237
202
  if (!opts.skipBrowser) {
238
203
  log('✔ Opening browser...');
239
204
  openBrowser(FRONTEND_URL);
240
205
  }
241
- // ── 8. Summary ────────────────────────────────────────────────────────────
242
206
  console.log('\n ' + '─'.repeat(58));
243
207
  console.log(' ✔ System ready');
244
208
  console.log(` Backend → ${BACKEND_URL}`);
245
209
  console.log(` UI → ${FRONTEND_URL}`);
246
210
  console.log(' ' + '─'.repeat(58));
247
211
  console.log('\n Press Ctrl+C to stop.\n');
248
- // ── Shutdown handler ──────────────────────────────────────────────────────
249
212
  const shutdown = () => {
250
213
  log('Shutting down...');
251
214
  if (!backendExited)
@@ -255,7 +218,6 @@ async function runStart(opts) {
255
218
  };
256
219
  process.on('SIGINT', shutdown);
257
220
  process.on('SIGTERM', shutdown);
258
- // Park indefinitely until Ctrl+C
259
221
  await new Promise(() => { });
260
222
  }
261
223
  //# sourceMappingURL=start.js.map
@@ -9,18 +9,7 @@ export interface AttackPathReport {
9
9
  avgHops: number;
10
10
  };
11
11
  }
12
- /**
13
- * Detects all attack paths in the graph that lead from an entry-point node
14
- * to a crown-jewel node using BFS (shortest path) per pair.
15
- *
16
- * Paths are sorted by descending riskScore.
17
- */
18
12
  export declare function detectAttackPaths(graph: Graph): AttackPath[];
19
- /**
20
- * Same as `detectAttackPaths` but also finds ALTERNATE (non-shortest) paths
21
- * for richer analysis. Returns a full report with statistics.
22
- */
23
13
  export declare function generateFullAttackReport(graph: Graph): AttackPathReport;
24
- /** Pretty-prints the top N attack paths to the console. */
25
14
  export declare function printAttackPaths(paths: AttackPath[], limit?: number): void;
26
15
  //# sourceMappingURL=attack-path.d.ts.map