Files
NeuroSploit/docs/graph-memory.md
T
CyberSecurityUPandClaude Opus 5.5 79685cee49 feat: graph memory in the test loop, wire-bound evidence, authz matrix, report redaction
Harness
- graph_memory: typed attack graph (web + internal network) with Neo4j sync
  over the HTTP API. Graph checkpoints run after recon, validation, each
  chaining round and at the end. They direct agent selection and feed the
  live chains, next moves and dependency todolist into every prompt.
- wire: every reported exchange is checked against recorded flows and
  re-sent through the scope-guarded replay engine. A contradiction means the
  finding cannot stand as confirmed.
- traces: each agent's transcript is shared with the others as a fenced,
  structured digest (endpoints, params, error signatures, versions).
- authz_matrix: role x route authorization matrix validator, with
  identity tiers in creds.
- deliverables: evidence custody, exploit-as-test (honest oracles only),
  detection content and personas, with no credential leakage.
- redact: credentials quoted by agents become $SECRET_n in every
  human-facing report (md/html/json/sarif/typ, findings.md). findings.json
  and the vault keep raw values for re-testing.
- compliance: GDPR replaces LGPD.
- New agents: authorization_matrix, session_lifecycle, api_token_structure,
  exposed_datastore, postgres_superuser_rce, meta/graph_reasoning.

Web console
- New tabs, mounted in both the detail and live views: Attack graph
  (replaces Generative Attack Path Chaining), Chains, Dependencies,
  Traffic, Traces, Deliverables.
- Wire/Status columns, KPIs, CSV export.
- Graph recompute / Neo4j push / Re-test actions, all through the CLI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015v24fuCWxkanS31HDmaDB6
2026-10-11 19:20:29 -03:00

8.2 KiB

Graph memory & attack-path reasoning (Neo4j)

NeuroSploit keeps three graphs: the knowledge graph (what was learned about a target, across runs), the internal graph (hosts, identities, credentials, the AD credential loop) and the chain links (which finding hands which capability to which). graph_memory.rs projects all three into one typed schema, reasons over it, and — when configured — MERGEs it into Neo4j and reads accumulated paths back.

It works without Neo4j. Every run writes graph/ next to the report:

file what
graph/attack-chains.md chains in the 11-field analyst format, plus next moves
graph/graph.cypher idempotent MERGE script: cypher-shell -a <uri> -u <user> -p <pw> -f graph.cypher
graph/graph.json nodes/edges as JSON
graph/queries.cypher ready-made analyst queries

The graph is a phase of the test

The pipeline runs a graph checkpoint at each stage, in every mode (black-box, grey-box, host/infra, container, mobile, AI):

checkpoint what goes in what it changes
recon recon surface (in-scope hosts, services, endpoints, tech, secret fingerprints) + this target's cross-run memory + saved internal graph + Neo4j paths black-box selection adds graph-directed agents (e.g. SSRF → ssrf_cloud, authz/identities → authorization_matrix, yielded secret → session_lifecycle, PostgreSQL+SQLi → postgres_superuser_rce, datastore tech → exposed_datastore)
validated the validated findings every chaining agent sees the new chains
chain round N each round's validated findings footholds on the most valuable chains are expanded first
final final findings (before secrets are masked) Neo4j sync, report

Each checkpoint publishes a live context that operator_directives adds to every exploit and chaining prompt: the surface, the top chains with their status, the next moves, and the graph discipline (report obtained credentials in secret/account, dependencies in chains_from, unproven steps as hypotheses). It also writes graph/live-chains.md and graph/live.json so the operator can watch the graph evolve, and MERGEs the run's slice into Neo4j when configured.

Dependency todolist (graph/todo.md). Every unconfirmed chain is laid out step by step: done when the graph holds evidence for the step, ready when every earlier step is done, blocked otherwise. The graph is the persistent list, so it survives rounds and restarts. The chaining rounds dispatch ready steps first, one per foothold, each with the exact step to perform and an instruction not to jump ahead; a step already attempted is not re-sent.

Shared traces (traces/<agent>.md). Each agent's transcript is written as it finishes. Every prompt carries a structured digest of the other agents' traces (endpoint paths, parameter names, error signatures, product versions, block signals — never raw lines), fenced as untrusted data, plus the directory to grep for full context.

Wire-bound evidence (always on, before validation). Each exchange an agent reports is checked against recorded traffic (when the recording interceptor ran) and re-sent by the harness itself through the scope-guarded replay engine (non-mutating verbs only; identities only when their credentials were recorded). When the wire reproduces the account, the harness's observation replaces it and the validators judge real bytes ([wire-verified]). When it does not, the disagreement is recorded and the finding cannot stand as confirmed. What cannot be checked stays as reported and is counted as unverifiable.

Cross-run memory is scoped to the target: only the component reachable from this target's asset node, through target-specific nodes, is used — another engagement's history never enters this one's prompts.

Using it

# During a run: chaining rounds get the graph's chains and next moves, and the
# result is pushed to Neo4j at the end.
export NEO4J_USER=neo4j NEO4J_PASSWORD='…'
neurosploit run https://app.example --neo4j bolt://localhost:7687

# After a run (offline, or with --push to MERGE + read back accumulated paths)
neurosploit graph <run-id|dir> [--internal internal-graph.json] [--push] [--top 10]

# Internal network: save the AD/host graph into the run, then reason over both
neurosploit internal --graph ad.json --from <node> --save runs/<id>/internal-graph.json
neurosploit graph <run-id>

Configuration: --neo4j <uri> or NEO4J_URI (bolt://, neo4j://, neo4j+s:// are mapped to the HTTP transactional endpoint on 7474/7473; http(s):// is used as is). Credentials only from NEO4J_USER (or NEO4J_USERNAME), NEO4J_PASSWORD, NEO4J_DATABASE — never from flags, never written to disk.

Schema

Labels: Target, Asset, Host, IPAddress, Domain, Service, Endpoint, Technology, Vulnerability, Finding, Evidence, Credential, Identity, Role, Permission, Secret, ExploitAttempt, ValidationResult, AttackStep, AttackPath, BusinessImpact, Control, Mitigation.

Relationships: HAS_ASSET, RESOLVES_TO, EXPOSES, RUNS, HOSTS, USES_TECH, HAS_ENDPOINT, HAS_VULNERABILITY, HAS_FINDING, SUPPORTED_BY, OBSERVED_IN, REQUIRES, ENABLES, YIELDS, CAN_ACCESS, HAS_PERMISSION, ESCALATES_TO, PIVOTS_TO, DEPENDS_ON, VALIDATED_BY, MITIGATED_BY, IMPACTS, PART_OF_PATH.

Stable keys (MERGE): Host = target + host; Service = host + port + proto; Endpoint = method + normalized URL (ids collapsed to {id}, query dropped); Vulnerability = CWE/type + endpoint; Finding = target + type + host + evidence hash; Evidence = hash of the observed exchange (write-once); Credential/Secret = salted fingerprint.

How web and internal meet: a finding whose evidence demonstrates code execution or internal reach gets AttackStep -CAN_ACCESS-> Host. The internal graph's node for the same host shares the key, so its credentials and pivots continue the path.

Reasoning rules (enforced in code)

  • Local vs chain severity. A chain is only scored above its findings' local severity when it is confirmed — every finding confirmed by the validation engine and every edge evidenced. Otherwise it keeps the local severity and states the severity it would have once validated (High — Critical once validated).
  • Impact must be demonstrated. An IMPACTS edge is confirmed only when the evidence reached that rung: code execution needs a nonce in output or an out-of-band callback; sensitive-data exposure needs sensitive data actually read. "Could lead to RCE" stays needs_validation.
  • Derived links are hypotheses. Capability-matched links between findings are needs_validation; cross-run memory is never more than needs_review.
  • Status. confirmed / needs_validation / hypothesis (rests on an unconfirmed finding, an unproven node, or history).
  • Next moves. The first unproven edge of the most valuable chains, yielded credentials not yet tried against in-scope services (lockout-aware), code execution not yet connected to the internal network, SSRF without a recorded internal target. These feed the attack-chaining rounds, and footholds on the most valuable chains are expanded first.

Data minimisation

No raw secret, token, cookie, password, response body or model narrative becomes a graph property. Secrets are fingerprints salted per installation (~/.neurosploit/graph-salt, or NEUROSPLOIT_GRAPH_SALT — share one value across a team that shares a Neo4j so credential reuse still correlates). Identities that look like personal data are masked (a***@corp.test). Endpoints lose their query strings. Internal credential nodes are named from the node id and kind, never from the label. Labels and relationship types come from closed enums; all values are statement parameters.

Queries

See graph/queries.cypher. Examples:

// Hypotheses still worth validating, best first
MATCH (p:AttackPath) WHERE p.status IN ['needs_validation','hypothesis']
RETURN p ORDER BY p.risk_score DESC;

// Credentials that reach privileged identities (internal)
MATCH path = (c)-[:CAN_ACCESS|HAS_PERMISSION|ESCALATES_TO*1..6]->(r)
WHERE (c:Credential OR c:Secret) AND (r:Role OR r:Identity) AND r.privileged = true
RETURN path;

The LLM-facing version of these rules is agents_md/meta/graph_reasoning.md.