refactor(server, scan): introduce target management with active target switching, enhance DB pool handling, and integrate target-aware task routes for improved modularity

This commit is contained in:
elipeter 2026-05-29 13:14:29 -05:00
parent acdc71cd88
commit 635b213825
40 changed files with 1810 additions and 240 deletions

View file

@ -9,6 +9,17 @@ Nyx ships four independent detector families. They run together in `--mode full`
| [State model](detectors/state.md) | `state-*` | Per-function state lattice | Use-after-close, double-close, leaks, unauthenticated access |
| [AST patterns](detectors/patterns.md) | `<lang>.<cat>.<name>` | Tree-sitter structural match | Banned APIs, weak crypto, dangerous constructs |
```mermaid
flowchart LR
Taint["Taint analysis<br/>cross-file source-to-sink"] --> Normalize["Normalize findings"]
Cfg["CFG structural<br/>guards, exits, resource paths"] --> Normalize
State["State model<br/>resource and auth lattice"] --> Normalize
Ast["AST patterns<br/>tree-sitter structural match"] --> Normalize
Normalize --> Dedupe["Deduplicate<br/>same site, rule, severity"]
Dedupe --> Rank["Rank<br/>severity, evidence, context"]
Rank --> Output["Console, JSON, SARIF, UI"]
```
The taint family is split into cap-specific rule classes when a sink callee carries multiple vulnerability classes:
| Rule id | Cap | Surface |

View file

@ -6,6 +6,21 @@ If you're going to act on a finding, it helps to know how the scanner got there.
A scan runs in two passes over the file tree, with an optional SQLite index that lets the second scan skip files whose content hash hasn't changed.
```mermaid
flowchart TD
Walk["Walk file tree"] --> Pass1["Pass 1 per file<br/>tree-sitter parse, CFG, SSA"]
Pass1 --> Summaries["Per-function summaries<br/>sources, sinks, sanitizers, returns, points-to"]
Pass1 --> Hierarchy["Type hierarchy index<br/>extends, implements, impl-for, includes"]
Summaries --> Global["GlobalSummaries map<br/>plus optional SQLite cache"]
Hierarchy --> Global
Global --> Pass2["Pass 2 per file<br/>cross-file context"]
Pass2 --> Taint["Forward SSA taint worklist<br/>finite lattice, guaranteed convergence"]
Pass2 --> Calls["Call precision<br/>k=1 inline, summaries, SCC fixed-point"]
Taint --> Findings["Findings with evidence<br/>source, path, sink, engine notes"]
Calls --> Findings
Findings --> Emit["Rank, dedupe, emit<br/>console, JSON, SARIF, UI"]
```
**Pass 1, per file.** Tree-sitter parses the file. Nyx builds an intra-procedural control-flow graph, lowers it to SSA, and extracts a summary per function describing what that function does at the boundary: which arguments flow to sinks, which sources it reads from, which sinks it calls, what taint it strips, what it returns. Summaries are persisted to SQLite ([`src/summary/`](https://github.com/elicpeter/nyx/tree/master/src/summary/), [`src/database.rs`](https://github.com/elicpeter/nyx/blob/master/src/database.rs)).
**Summary merge.** All per-file summaries get unioned into a global map keyed by qualified function name.

69
docs/mermaid-init.js Normal file
View file

@ -0,0 +1,69 @@
(function () {
const MERMAID_URL =
"https://cdn.jsdelivr.net/npm/mermaid@10.9.3/dist/mermaid.esm.min.mjs";
async function renderMermaid() {
const blocks = Array.from(
document.querySelectorAll("pre > code.language-mermaid"),
);
if (blocks.length === 0) {
return;
}
try {
const mermaidModule = await import(MERMAID_URL);
const mermaid = mermaidModule.default;
mermaid.initialize({
startOnLoad: false,
securityLevel: "strict",
theme: "base",
themeVariables: {
background: "transparent",
fontFamily:
"Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif",
primaryColor: "#0f172a",
primaryTextColor: "#e5e7eb",
primaryBorderColor: "#22d3ee",
secondaryColor: "#134e4a",
secondaryTextColor: "#e5e7eb",
secondaryBorderColor: "#2dd4bf",
tertiaryColor: "#1e293b",
tertiaryTextColor: "#e5e7eb",
tertiaryBorderColor: "#64748b",
lineColor: "#94a3b8",
edgeLabelBackground: "#0f172a",
clusterBkg: "#111827",
clusterBorder: "#475569",
},
});
for (const block of blocks) {
const pre = block.parentElement;
if (!pre) {
continue;
}
const wrapper = document.createElement("div");
wrapper.className = "nyx-mermaid";
const diagram = document.createElement("div");
diagram.className = "mermaid";
diagram.textContent = block.textContent.trim();
wrapper.appendChild(diagram);
pre.replaceWith(wrapper);
}
await mermaid.run({ querySelector: ".nyx-mermaid .mermaid" });
} catch (error) {
console.warn("Mermaid rendering failed", error);
}
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", renderMermaid);
} else {
renderMermaid();
}
})();

15
docs/mermaid.css Normal file
View file

@ -0,0 +1,15 @@
.nyx-mermaid {
margin: 1.5rem 0;
padding: 1rem;
overflow-x: auto;
border: 1px solid rgba(148, 163, 184, 0.35);
border-radius: 8px;
background: rgba(15, 23, 42, 0.28);
}
.nyx-mermaid svg {
display: block;
max-width: 100%;
height: auto;
margin: 0 auto;
}

View file

@ -11,6 +11,16 @@ nyx serve --no-browser # don't auto-open
Persistent settings live under `[server]` in `nyx.conf` / `nyx.local`.
```mermaid
flowchart LR
Scan["nyx scan<br/>or UI-started scan"] --> Cache[".nyx findings<br/>plus SQLite project index"]
Cache --> Serve["nyx serve<br/>loopback API and embedded React UI"]
Serve --> Review["Review findings<br/>flow, evidence, history"]
Review --> Triage["Update triage state<br/>investigate, suppress, accept, fix"]
Triage --> Sync[".nyx/triage.json<br/>optional repo-synced state"]
Sync --> Cache
```
Starting a scan from the UI runs dynamic verification on `Confidence >= Medium`
findings by default. Check "Skip dynamic verification" in the scan modal to get
a fast static-only result. See [Dynamic verification](dynamic.md) for details.