Skip to content

Use EXPLAIN-based validation as primary Cypher query security boundary #484

@JeroenDeDauw

Description

@JeroenDeDauw

Below description comes from C Opus 45 based on my prompt and is approved.

Problem

CypherQueryFilter uses a keyword blocklist to reject non-read queries. This is inherently fragile:
new Neo4j versions can introduce new write/admin keywords, and it's easy to miss one. Issues #479 and
#480 are symptoms of this — we keep discovering keywords we forgot to block.

Proposed approach

Use Neo4j's own query planning as the primary security boundary. Before executing a user-provided
Cypher query, run EXPLAIN <query> against Neo4j. The Bolt protocol result summary includes a
queryType field (r, w, rw, s). If it's not r, reject.

This delegates parsing to the one authority that definitively knows what constitutes a write — Neo4j
itself. No keyword list to maintain, and it's future-proof against new syntax.

Tradeoffs

  • One extra round-trip per user query (EXPLAIN then execute). Acceptable for #cypher_raw since a
    Neo4j call is already being made.
  • Need to verify the Laudis PHP client exposes queryType from the result summary.
  • EXPLAIN itself should be safe (read-only by design), but worth verifying.

Role of existing CypherQueryFilter

Keep the keyword filter as a fast-fail / defense-in-depth layer. It rejects obviously bad queries
without hitting Neo4j. But it no longer needs to be the sole security boundary, so missing a keyword
becomes "slightly wasteful round-trip" rather than "security hole."

SHOW as a special case

SHOW queries (e.g. SHOW USERS, SHOW DATABASES) are read-only from Neo4j's perspective, so
EXPLAIN would classify them as r. If blocking information disclosure is desired, the keyword filter
is still needed for that — it's a policy decision beyond read-vs-write.

AC

  • User-provided Cypher queries are validated via EXPLAIN before execution
  • Non-read queries are rejected based on the EXPLAIN result
  • CypherQueryFilter keyword blocklist is retained as secondary defense
  • Tests

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions