jsonpath
jsonpath - Secure JSONPath Query Engine for Node.js
Repository Health
Technical Analysis
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, andapply— 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-evalwith 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 ofArray.prototype.filterboilerplate - Rewriting or vivifying values at a computed path with
jp.value()orjp.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.
Used by 2 apps in this directory
Dittofeed
Marketing · Automation
Open-source omni-channel customer engagement platform for automating transactional and marketing messages via email, SMS, WhatsApp, Slack, and mobile push.
Ghostfolio
Invoicing Finance
Track your stocks, ETFs, and crypto with a privacy-first, self-hostable wealth management platform built for data-driven investors.