jsonpath

jsonpath - Secure JSONPath Query Engine for Node.js

Library
npm
v1.3.0
1,430stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
43/100Fair
Development Activity4
Maintenance0
Community68
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
66/100Good
Architecture78
Code Quality68
Innovation74
Learning Curve45

jsonpath is a JSONPath implementation for Node.js that lets you query, extract, and mutate JavaScript objects using Stefan Goessner’s JSONPath syntax — the same $.., wildcard, slice, and filter-expression grammar used across JSONPath tooling in other languages. It exposes query, nodes, paths, value, parent, and apply methods built on a formal BNF grammar (via Jison) rather than the original regex-based parser, so expressions are parsed consistently instead of pattern-matched.

Because JSONPath expressions can embed script and filter expressions ((...) and ?(...)), the library evaluates them through a hardened AST walker that explicitly rejects code-execution vectors — function calls, new, this, template literals — and blocks access to __proto__, constructor, and prototype at every entry point (subscripts, unions, filter expressions, and vivification), closing prototype-pollution and sandbox-escape paths that plagued similar libraries.

What You Get

  • A complete JSONPath implementation covering root/child/descendant member access, wildcards, array slices, unions, and filter/script expressions
  • Six core methods — query, nodes, paths, value, parent, and apply — for reading, locating, and mutating matched values
  • A formal BNF grammar (built with Jison) instead of regex pattern-matching, so parsing behavior is predictable and documented
  • Hardened expression evaluation via static-eval with an explicit AST allowlist that blocks prototype pollution and code-injection vectors
  • A prebuilt browser bundle (jsonpath.js/jsonpath.min.js) alongside the Node.js module for use outside a bundler

Common Use Cases

  • Extracting specific fields (like all prices or all authors) from deeply nested API responses without writing recursive traversal code
  • Filtering arrays of objects with conditional expressions, e.g. $..book[?(@.price<10)], instead of Array.prototype.filter boilerplate
  • Rewriting or vivifying values at a computed path with jp.value() or jp.apply() when the exact path isn’t known ahead of time
  • Safely evaluating user-supplied or config-driven JSONPath expressions against trusted data without exposing prototype-pollution or code-execution surface

Under The Hood

Architecture The pipeline is a clean three-stage flow: lib/parser.js wraps a Jison-generated parser (compiled from the BNF grammar in lib/grammar.js) that turns a JSONPath string into an array of path components; JSONPath.prototype.nodes in lib/index.js folds those components left-to-right over a working set of {path, value} partials; and lib/handlers.js resolves each component to a traversal function via a lookup key built from operation-scope-expression.type (e.g. member-child-identifier). Safety checks (_assert_safe_key, _assert_safe_components) are threaded through every public method in index.js, not bolted on separately, so value(), apply(), and parent() all reject __proto__/constructor/prototype keys before touching the target object. The one architectural wrinkle is a couple of require('..')/require('./index') calls inside handlers.js (for descendant unions and script-expression recursion), creating a circular dependency between the handler and index modules that would need care if the AST shape ever changed.

Tech Stack Plain CommonJS Node.js module with only three runtime dependencies: esprima (wrapped by lib/aesprim.js) parses script/filter expression source into a JS AST, static-eval walks that AST against a {'@': value} scope, and underscore supplies _.uniq for de-duplicating union results. Build tooling is Grunt-based (Gruntfile.js, grunt-browserify, grunt-contrib-uglify) to produce the browser bundle, plus Jison (invoked via bin/generate_parser.js) to compile the grammar into generated/parser.js. Tests run under Mocha in TDD style (suite/test), linting via JSHint and JSCS, and CI is wired through Travis targeting Node 0.10 — a stale config typical of a long-lived, low-churn library.

Code Quality The test suite is substantial (test/query.js, parse.js, stringify.js, slice.js, sugar.js, security.js) and test/security.js in particular is a dense, deliberately-maintained regression suite labeled against a specific CVE, covering dozens of prototype-pollution and code-injection bypass attempts — bracket notation, unicode-escaped property names, JSFuck-style access, IIFEs, tagged templates, sequence expressions. Error handling favors explicit throw new Error(...) with descriptive messages over silent failure, and npm test runs both Mocha and JSHint. The codebase is untyped, ES5-style JavaScript with no TypeScript definitions, and its Travis CI target (Node 0.10) no longer reflects a runnable modern pipeline.

What Makes It Unique Unlike many JSONPath libraries that lean on the original regex-based implementation (which is forgiving in some cases and outright wrong in others, per the README’s own comparison), this project reverse-engineered a formal grammar to parse expressions deterministically. Its standout trait is defense-in-depth around script/filter expressions: rather than trusting static-eval alone, it adds its own AST allowlist (isSafeAst) that explicitly enumerates safe node types and explicitly rejects call expressions, this, assignment, and template literals — treating every unnamed property-access pattern as untrusted by default rather than relying on a single denylist.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search