diff --git a/.babelrc b/.babelrc.json
similarity index 100%
rename from .babelrc
rename to .babelrc.json
diff --git a/.editorconfig b/.editorconfig
index 65e12994..5265f4e9 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -8,9 +8,9 @@ charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
+; indent_style = tab
indent_size = 4
trim_trailing_whitespace = true
-; [app/public/css/**.styl]
-; indent_style = tab
-; indent_size = 2
+[*.{json,yml}]
+indent_size = 2
diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index f5b9a9b2..00000000
--- a/.eslintignore
+++ /dev/null
@@ -1,3 +0,0 @@
-node_modules
-dist
-docs/ts
diff --git a/.eslintrc.js b/.eslintrc.js
deleted file mode 100644
index 2bc9be91..00000000
--- a/.eslintrc.js
+++ /dev/null
@@ -1,72 +0,0 @@
-module.exports = {
- "extends": ["ash-nazg/sauron-node"],
- "settings": {
- "polyfills": [
- "Array.isArray",
- "console",
- "Date.now",
- "document.head",
- "document.querySelector",
- "JSON",
- "Object.keys",
- "XMLHttpRequest"
- ]
- },
- "overrides": [
- {
- "files": ["src/jsonpath.js", "test-helpers/node-env.js"],
- // Apparent bug with `overrides` necessitating this
- "globals": {
- "require": "readonly",
- "module": "readonly"
- }
- },
- {
- "files": ["*.md"],
- "rules": {
- "import/unambiguous": 0,
- "import/no-commonjs": 0,
- "import/no-unresolved": ["error", {"ignore": ["jsonpath-plus"]}],
- "no-undef": 0,
- "no-unused-vars": ["error", {
- "varsIgnorePattern": "json|result"
- }],
- "node/no-missing-require": ["error", {
- "allowModules": ["jsonpath-plus"]
- }],
- "node/no-missing-import": ["error", {
- "allowModules": ["jsonpath-plus"]
- }]
- }
- },
- {
- "files": ["test/**"],
- "globals": {
- "assert": "readonly",
- "jsonpath": "readonly",
- "require": "readonly",
- "module": "readonly"
- },
- "parserOptions": {
- "sourceType": "script"
- },
- "env": {"mocha": true},
- "rules": {
- "strict": ["error", "global"],
- "import/no-commonjs": 0,
- "import/unambiguous": 0,
- "quotes": 0,
- // Todo: Reenable
- "max-len": 0
- }
- }
- ],
- "rules": {
- "indent": ["error", 4, {"outerIIFEBody": 0}],
- "promise/prefer-await-to-callbacks": 0,
- "quote-props": 0,
- "require-jsdoc": 0,
- // Reenable when no longer having problems
- "unicorn/no-unsafe-regex": 0
- }
-};
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..2c9d15d4
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: [brettz9] # May have up to 4 comma-separated user names
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..98904fef
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,44 @@
+---
+name: Bug report
+about: Create a report in case we may be able to help
+labels: Bug - unconfirmed
+
+---
+
+
+## Describe the bug
+
+
+
+## Code sample or steps to reproduce
+
+```js
+// Code that reproduces problem here
+```
+
+### Console error or logs
+
+## Expected behavior
+
+
+
+## Expected result
+
+```json
+
+```
+
+## Environment (IMPORTANT)
+- JSONPath-Plus version: [e.g. 4.0.0]
+
+## Desktop**
+ - OS: [e.g. Windows]
+ - Browser and version [e.g. chrome 65] or Node Version [e.g. 10.2]
+
+## Additional context
+
+
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..a89195d9
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,36 @@
+---
+name: Feature Request
+about: Report a new feature
+title: ''
+labels: Feature
+assignees: ''
+---
+
+
+
+## Motivation
+
+
+
+## Current behavior
+
+
+
+## Desired behavior
+
+
+
+## Alternatives considered
+
+
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 00000000..c6a26cca
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,10 @@
+## PR description
+
+
+
+
+## Checklist
+
+- [ ] - Added tests
+- [ ] - Ran `npm test`, ensuring linting passes
+- [ ] - Adjust README documentation if relevant
diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml
new file mode 100644
index 00000000..bc9ea90d
--- /dev/null
+++ b/.github/workflows/node.js.yml
@@ -0,0 +1,30 @@
+name: Node.js CI
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ node-version:
+ - 12.x
+ - 14.x
+ - 16.x
+ steps:
+ - uses: actions/checkout@v2
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+ - name: Setup PNPM
+ uses: pnpm/action-setup@v1.2.1
+ with:
+ version: 4.11.1
+ - run: pnpm i
+ - run: pnpm run build --if-present
+ - run: pnpm test
diff --git a/.gitignore b/.gitignore
index 19e78f5b..54810e9f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,11 @@
*.iml
.DS_Store
.idea
+ignore
temp
node_modules
pids
reports
target
*.log
-docs/ts
+coverage
diff --git a/.mocharc.cjs b/.mocharc.cjs
new file mode 100644
index 00000000..97758a1d
--- /dev/null
+++ b/.mocharc.cjs
@@ -0,0 +1,5 @@
+'use strict';
+
+module.exports = {
+ reporter: 'mocha-multi-reporters'
+};
diff --git a/.nojekyll b/.nojekyll
new file mode 100644
index 00000000..e69de29b
diff --git a/.npmignore b/.npmignore
index 7213177c..614e4f74 100644
--- a/.npmignore
+++ b/.npmignore
@@ -5,7 +5,21 @@ rollup.config.js
lgtm.yml
.travis.yml
.DS_Store
-.babelrc
+.babelrc.json
.idea
-.remarkrc
docs
+.nyc_output
+coverage
+badges
+mocha-multi-reporters.json
+.mocharc.cjs
+.github
+.nojekyll
+ignore
+pnpm-lock.yaml
+eslint.config.js
+.editorconfig
+.eslintignore
+licenseInfo.json
+tsconfig.json
+demo
diff --git a/.remarkrc b/.remarkrc
deleted file mode 100644
index 0a489d05..00000000
--- a/.remarkrc
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "plugins": {
- "lint-code-block-style": "fenced",
- "lint-ordered-list-marker-value": "one"
- }
-}
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 77353b18..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-sudo: false
-language: node_js
-node_js:
- - 6
- - 8.3
- - 10
- - 12
-
-before_install:
- - npm config set depth 0
-before_script:
- - 'if [ "${ESLINT-}" == "6" ]; then npm install --no-save "eslint@${ESLINT}.1" ; fi'
- - 'if [ "${ESLINT-}" == "5" ]; then npm install --no-save "eslint@${ESLINT}" eslint-plugin-unicorn@8.0.2 @typescript-eslint/parser@1.13.0 ; fi'
-script:
- - npm run test
- - 'if [ -n "${LINT-}" ]; then npm run eslint; fi'
-env:
- matrix:
- - ESLINT=6
- - ESLINT=5
-matrix:
- fast_finish: true
- include:
- - node_js: 'lts/*'
- env: LINT=true
- exclude:
- - node_js: 6
- env: ESLINT=6
diff --git a/CHANGES.md b/CHANGES.md
index b202715d..f8d7a0f1 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,242 @@
# CHANGES for jsonpath-plus
-## 2.0.0 (November 23, 2019)
+## 10.4.0
+
+- chore: update devDeps.
+- docs: fix Markdown formatting of examples in README.md (#230) (@aspiers)
+- feat: add void operator (#244) (@80avin)
+- build(deps): bump qs from 6.14.0 to 6.14.1 (#248) (@dependabot[bot])
+- chore: update security policy (#250) (@80avin)
+- build(deps): bump lodash from 4.17.21 to 4.17.23 (#249) (@dependabot[bot])
+- fix(eval): rce using lookupGetter or lookupSetter ([1bab1cc](https://github.com/JSONPath-Plus/JSONPath/commit/1bab1cc835502530faaabdac8d8706505ab82a55)) (@80avin)
+
+## 10.3.0
+
+- fix(eval): rce using non-string prop names (#237)
+- feat(demo): make demo link shareable (#238)
+- chore: update deps. and devDeps.
+
+## 10.2.0
+
+- fix(eval): improve security of safe-eval (#233)
+- chore: update deps. and devDeps.
+
+## 10.1.0
+
+- feat: add typeof operator to safe script
+
+## 10.0.7
+
+- fix(security): prevent `constructor` access
+- docs: add security policy file
+
+## 10.0.6
+
+- fix(security): prevent `call`/`apply` invocation of `Function`
+
+## 10.0.5
+
+- fix: remove overly aggressive disabling of native functions but
+ disallow `__proto__`
+
+## 10.0.4
+
+- fix(security): further prevent binding of Function calls which may evade detection
+
+## 10.0.3
+
+- fix(security): prevent binding of Function calls which may evade detection
+
+## 10.0.2
+
+- fix(security): prevent Function calls outside of member expressions
+
+## 10.0.1
+
+- fix(security): prohibit `Function` in "safe" vm
+
+## 10.0.0
+
+BREAKING CHANGES:
+- Require Node 18+
+
+- fix(security): use safe vm by default in Node
+- chore: bump jsep, devDeps. and lint
+
+## 9.0.0
+
+BREAKING CHANGES:
+- Removes `preventEval` property. Prefer `eval: false` instead.
+- Changed behavior of `eval` property. In the browser, `eval`/`Function` won't be used by default to evaluate expressions. Instead, we'll safely evaluate using a subset of JavaScript. To resume using unsafe eval in the browser, pass in the option `eval: "native"`
+
+- feat: add safe eval for browser and `eval` option (#185) (@80avin)
+- feat: add `ignoreEvalErrors` property (@80avin)
+
+## 8.1.0
+
+- feat: add basic cli (#206) (@vid)
+
+## 8.0.0
+
+- Breaking change: Bump Node `engines` to 14
+- feat: add support for nested filter expressions (@carlosingles)
+- docs: update README and license (@akirataguchi115)
+- docs: github workflow badge (@dsanch3z)
+
+## 7.2.0
+
+- perf: optimize walk method by 10%-34% (@jacobroschen)
+- chore: add types to exports field (@awlayton)
+
+## 7.1.0
+
+- perf: improve evaluation speed of conditional queries (@jacobroschen)
+
+## 7.0.0
+
+- Breaking change: Bump `engines` to 12
+- fix: remove `console.log` when error is thrown (@sh33dafi)
+- chore: update devDeps.
+
+## 6.0.1 (2021-07-07)
+
+- Fix: Some `package.json` paths needed updating (@matushorvath)
+- npm: Update devDeps.
+
+## 6.0.0 (2021-07-05)
+
+### User-impacting
+
+- Breaking enhancement: Create as true ESM module
+- Breaking change: Utilize `.cjs` extension for UMD and CJS builds (very
+ old browsers might not support, but needed with the change given that
+ Webpack may complain if there even exists CJS within what it thinks is
+ an ESM file, the ".js", our default)
+- Breaking change: Utilize `.js` extension instead of `.mjs` for now default
+ ESM builds
+
+### Dev-impacting
+
+- npm: Add `lint` script
+- npm: Update devDeps.
+
+## 5.1.0 (2021-06-24)
+
+- Enhancement: support double-quoted bracket notation
+- Linting: As per latest ash-nazg
+- npm: Update devDeps.
+
+## 5.0.7 (2021-04-12)
+
+- Fix: Add `packge.json` to `exports` (@sebastiendavid)
+
+## 5.0.6 (2021-04-09)
+
+- Fix: Remove `static` modifiers (@sdolski)
+- Linting: As per latest ash-nazg
+- npm: Update devDeps.
+
+## 5.0.5 (2021-04-09)
+
+- Fix: Avoid cache corruption when the returned structure is modified.
+ Fixes #102. (@tejodorus)
+
+## 5.0.4 (2021-03-02)
+
+- Fix: allow falsey at values in filter (now may require checking for
+ presence of `@` in some cases); fixes #136
+- Docs: Add old missing release info (reconciling with GitHub releases)
+- Docs: Update README to reflect 1.2.0 was not a released version (subsume
+ release details into 2.0.0)
+- Linting: As per latest ash-nazg
+- npm: Update devDeps.
+
+## 5.0.3 (2021-02-06)
+
+- Fix: Add package exports for browser and umd (#145) (@gjvoosten)
+- Update: Build as per refactoring
+- Docs: Update as per typedoc update
+- Docs: Update license badges per latest
+- Linting: As per latest ash-nazg
+- CI: Update from Travis -> GitHub Actions
+- npm: Switch from `eslint-plugin-sonarjs` to `eslint-plugin-radar`
+- npm: Switch to pnpm
+- npm: Update devDeps.
+
+## 5.0.2 (2021-01-15)
+
+- Fix: Proper Node CommonJS export; fixes #144
+
+## 5.0.1 (2021-01-15)
+
+- Fix: Proper Node CommonJS export; fixes #143
+- Docs: Properly indicate new browser paths
+
+## 5.0.0 (2021-01-14)
+
+- Breaking change: Add `type: 'commonjs'` and `exports: {import, require}`
+ (with `node-import-test` npm script to demo)
+- Breaking change: Change paths for browser (now is
+ `dist/index-browser-umd.js` or `dist/index-browser-es.js`)
+ (for Node, `main` and `module` point to new Node-specific dist)
+- Breaking enhancement: Add `browser` for browser bundling;
+ allowing static analysis environments, doesn't have however
+ conditional code to require `vm`); for ESM browser bundling,
+ now must check `browser` in Rollup Node resolver plugin;
+ see README
+- Build: Update per latest devDeps.
+- Docs: Add Regex (`.match`) example on value (@jeffreypriebe)
+- Docs: Add Regex (`.match`) example on property
+- Docs: Fix XPath example (@humbertoc-silva)
+- Docs: Link to XPath 2.0 tester
+- Docs: Update badges per latest updates
+- Linting: quote props
+- Linting: As per latest ash-nazg
+- Testing: Fix browser tests
+- Testing: Add test case for setting values in callbacks (issue #126)
+- Testing: Add more at-sign tests
+- Testing: Bump timeout
+- Travis: Check Node 14
+- Travis: add default `dist` field to avoid extra config reporting
+- npm: Update from deprecated `rollup-plugin-babel` to `@rollup/plugin-babel`
+ (and make `babelHelpers` explicit)
+- npm: Reorder scripts by test execution order
+- npm: Update devDeps
+
+## 4.0.0 (2020-04-09)
+
+- Breaking change/fix: Disallow `resultType` from being lower-cased
+ (broke `parentProperty`)
+- Breaking change: Expect Node >= 10
+- Build: As per latest rollup
+- Linting: Check hidden files; update as per latest ash-nazg
+- Docs: Update coverage badge
+- npm: Update devDeps
+
+## 3.0.0 (2020-01-13)
+
+- Breaking change: Expect Node >= 8
+- Fix: Require `json` as "own" property
+- Fix: wrap: false returning inconsistent data types (@CacheControl)
+- Fix: Ensure throwing with a bad result type
+- Fix: Allow empty string keys
+- Fix: Avoid erring when value before parent selector is falsey
+- Fix: If `resultType` is "all", if path resolves internally to a
+ non-array (string), ensure it is converted to an array before
+ converting to pointer for `pointer`
+- Enhancement: Allow path as array in non-object signature
+- Docs: Add locally-generated badges for testing, coverage, etc.
+- Linting (ESLint): As per latest ash-nazg
+- Linting (ESLint): Remove redundant "use strict" with switch to ESM
+- Maintenance: 2 sp. for package.json
+- Testing: Add nyc for coverage
+- Testing: Test against source (using `esm`)
+- Testing: Improve coverage (more type operator tests)
+- Testing: Check vm
+- npm: Add `test-cov` script
+- npm: Update devDeps
+
+## 2.0.0 (2019-11-23)
- Breaking change: Throw `TypeError` instead of `Error` for missing
`otherTypeCallback` when using `@other`
@@ -8,18 +244,12 @@
- Enhancement: Throw `TypeError` for missing `json` (fixes #110)
- Enhancement: Use more efficient `new Function` over `eval`;
also allows use of cyclic context objects
+- Enhancement: Add `@root` filter selector
- Maintenance: Add `.editorconfig`
- Docs: Document options in jsdoc; add return values to callbacks;
fix constructor doc sig.
- Testing: Add test for missing `path` or `json`
- Testing: Remove unneeded closures
-- npm: Update devDeps
-
-## 1.2.0 (October 13, 2019)
-
-- Enhancement: Add `@root` filter selector
-- Enhancement: Use more efficient `new Function` over `eval`;
- also allows use of cyclic context objects
- npm: Update devDeps and `package-lock.json`
## 1.1.0 (September 26, 2019)
@@ -37,6 +267,10 @@
- Add TypeScript declaration
+## 0.20.2 (July 9, 2019)
+
+- `supportsNodeVM` check that works in GOJA, node and ReactNative. (@legander)
+
## 0.20.1 (June 12, 2019)
- npm: Avoid adding `core-js-bundle` as peerDep. (fixes #95)
diff --git a/LICENSE-MIT.txt b/LICENSE
similarity index 98%
rename from LICENSE-MIT.txt
rename to LICENSE
index 1862fb48..65b363b1 100644
--- a/LICENSE-MIT.txt
+++ b/LICENSE
@@ -1,4 +1,4 @@
-The MIT License (MIT)
+MIT License
Copyright (c) 2011-2019 Stefan Goessner, Subbu Allamaraju, Mike Brevoort,
Robert Krahn, Brett Zamir, Richard Schneider
diff --git a/README.md b/README.md
index 2a72d37d..dc5bf360 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,18 @@
-# JSONPath Plus [](http://travis-ci.org/s3u/JSONPath)
+[](https://www.npmjs.com/package/jsonpath-plus)
+
+[](badges/tests-badge.svg)
+[](badges/coverage-badge.svg)
+
+[](https://snyk.io/test/github/s3u/JSONPath)
+
+
+[](badges/licenses-badge.svg)
+
+[](https://github.com/JSONPath-Plus/JSONPath/actions/workflows/node.js.yml)
+
+(see also [licenses for dev. deps.](https://raw.githubusercontent.com/s3u/JSONPath/master/badges/licenses-badge-dev.svg?sanitize=true))
+
+# JSONPath Plus
Analyse, transform, and selectively extract data from JSON
documents (and JavaScript objects).
@@ -7,6 +21,13 @@ documents (and JavaScript objects).
additional operators and makes explicit some behaviors the original
did not spell out.
+Try the [browser demo](https://jsonpath-plus.github.io/JSONPath/demo/) or
+[Runkit (Node)](https://npm.runkit.com/jsonpath-plus).
+
+***Please note: This project is not currently being actively maintained. We
+may accept well-documented PRs or some simple updates, but are not looking
+to make fixes or add new features ourselves.***
+
## Features
* **Compliant** with the original jsonpath spec
@@ -37,6 +58,7 @@ did not spell out.
* Option for **callback to handle results** as they are obtained.
## Benchmarking
+
`jsonpath-plus` is consistently performant with both large and small datasets compared to other json querying libraries per [json-querying-performance-testing](https://github.com/andykais/json-querying-performance-testing). You can verify these findings by [running the project yourself](https://github.com/andykais/json-querying-performance-testing#how-to-run) and adding more perf cases.
## Install
@@ -57,17 +79,16 @@ const result = JSONPath({path: '...', json});
### Browser
-For browser usage you can directly include `dist/index-umd.js`; no Browserify
-magic is necessary:
+For browser usage you can directly include `dist/index-browser-umd.cjs`; no
+Browserify magic is necessary:
```html
-
-
+
-
```
@@ -77,14 +98,22 @@ You may also use ES6 Module imports (for modern browsers):
```html
```
### ESM (Bundlers)
-Or if you are bundling your JavaScript (e.g., with Rollup), just use:
+Or if you are bundling your JavaScript (e.g., with Rollup), just use,
+noting that [`mainFields`](https://github.com/rollup/plugins/tree/master/packages/node-resolve#mainfields)
+should include `browser` for browser builds (for Node, the default, which
+checks `module`, should be fine):
```js
import {JSONPath} from 'jsonpath-plus';
@@ -111,6 +140,8 @@ the callback function being executed 0 to N times depending
on the number of independent items to be found in the result.
See the docs below for more on `JSONPath`'s available arguments.
+See also the [API docs](https://jsonpath-plus.github.io/JSONPath/docs/ts/).
+
### Properties
The properties that can be supplied on the options object or
@@ -127,7 +158,7 @@ evaluate method (as the first argument) include:
- ***resultType*** (**default: "value"**) - Can be case-insensitive form of
"value", "path", "pointer", "parent", or "parentProperty" to determine
respectively whether to return results as the values of the found items,
- as their absolute paths, as [JSON Pointers](http://www.rfc-base.org/txt/rfc-6901.txt)
+ as their absolute paths, as [JSON Pointers](https://tools.ietf.org/html/rfc6901)
to the absolute paths, as their parent objects, or as their parent's
property name. If set to "all", all of these types will be returned on
an object with the type as key name.
@@ -145,11 +176,20 @@ evaluate method (as the first argument) include:
it is necessary to distinguish between a result which is a failure
and one which is an empty array), it is recommended to switch the
default to `false`.
-- ***preventEval*** (**default: false**) - Although JavaScript evaluation
- expressions are allowed by default, for security reasons (if one is
- operating on untrusted user input, for example), one may wish to
- set this option to `true` to throw exceptions when these expressions
- are attempted.
+- ***eval*** (**default: "safe"**) - Script evaluation method.
+ `safe`: In browser, it will use a minimal scripting engine which doesn't
+ use `eval` or `Function` and satisfies Content Security Policy. In NodeJS,
+ it has no effect and is equivalent to native as scripting is safe there.
+ `native`: uses the native scripting capabilities. i.e. unsafe `eval` or
+ `Function` in browser and `vm.Script` in nodejs. `false`: Disable JavaScript
+ evaluation expressions and throw exceptions when these expressions are attempted.
+ `callback [ (code, context) => value]`: A custom implementation which is called
+ with `code` and `context` as arguments to return the evaluated value.
+ `class`: A class which is created with `code` as constructor argument and code
+ is evaluated by calling `runInNewContext` with `context`.
+ ``
+- ***ignoreEvalErrors*** (**default: false**) - Ignore errors encountered during
+ script evaluation.
- ***parent*** (**default: null**) - In the event that a query could be
made to return the root node, this allows the parent of that root node
to be returned within results.
@@ -196,7 +236,7 @@ evaluate method (as the first argument) include:
constructions `~` and `^` and type operators like `@string()` are
silently stripped.
- ***JSONPath.toPointer(pathAsArray)*** - Accepts a path array and
- converts to a [JSON Pointer](http://www.rfc-base.org/txt/rfc-6901.txt).
+ converts to a [JSON Pointer](https://tools.ietf.org/html/rfc6901).
The string will be in a form like: `/aProperty/anotherProperty/0`
(with any `~` and `/` internal characters escaped as per the JSON
Pointer spec). The JSONPath terminal constructions `~` and `^` and
@@ -284,35 +324,39 @@ and the following XML representation:
Please note that the XPath examples below do not distinguish between
retrieving elements and their text content (except where useful for
-comparisons or to prevent ambiguity).
-
-| XPath | JSONPath | Result | Notes |
-| ----------------- | ---------------------- | ------------------------------------- | ----- |
-/store/book/author | $.store.book\[*].author | The authors of all books in the store | Can also be represented without the `$.` as `store.book[*].author` (though this is not present in the original spec); note that some character literals (`$` and `@`) require escaping, however
-//author | $..author | All authors |
-/store/* | $.store.* | All things in store, which are its books (a book array) and a red bicycle (a bicycle object).|
-/store//price | $.store..price | The price of everything in the store. |
-//book\[3] | $..book\[2] | The third book (book object) |
-//book\[last()] | $..book\[(@.length-1)] $..book\[-1:] | The last book in order.| To access a property with a special character, utilize `[(@['...'])]` for the filter (this particular feature is not present in the original spec)
-//book\[position()<3]| $..book\[0,1] $..book\[:2]| The first two books |
-//book/*\[self::category\|self::author] or //book/(category,author) in XPath 2.0 | $..book\[0]\[category,author]| The categories and authors of all books |
-//book\[isbn] | $..book\[?(@.isbn)] | Filter all books with an ISBN number | To access a property with a special character, utilize `[?@['...']]` for the filter (this particular feature is not present in the original spec)
-//book\[price<10] | $..book\[?(@.price<10)] | Filter all books cheaper than 10 |
-| //\*\[name() = 'price' and . != 8.95] | $..\*\[?(@property === 'price' && @ !== 8.95)] | Obtain all property values of objects whose property is price and which does not equal 8.95 |
-/ | $ | The root of the JSON object (i.e., the whole object itself) | To get a literal `$` (by itself or anywhere in the path), you must use the backtick escape
-//\*/\*\|//\*/\*/text() | $..* | All Elements (and text) beneath root in an XML document. All members of a JSON structure beneath the root. |
-//* | $.. | All Elements in an XML document. All parent components of a JSON structure including root. | This behavior was not directly specified in the original spec
-//*\[price>19]/.. | $..\[?(@.price>19)]^ | Parent of those specific items with a price greater than 19 (i.e., the store value as the parent of the bicycle and the book array as parent of an individual book) | Parent (caret) not present in the original spec
-/store/*/name() (in XPath 2.0) | $.store.*~ | The property names of the store sub-object ("book" and "bicycle"). Useful with wildcard properties. | Property name (tilde) is not present in the original spec
-/store/book\[not(. is /store/book\[1\])\] (in XPath 2.0) | $.store.book\[?(@path !== "$\[\'store\']\[\'book\']\[0]")] | All books besides that at the path pointing to the first | @path not present in the original spec
-//book\[parent::\*/bicycle/color = "red"]/category | $..book\[?(@parent.bicycle && @parent.bicycle.color === "red")].category | Grabs all categories of books where the parent object of the book has a bicycle child whose color is red (i.e., all the books) | @parent is not present in the original spec
-//book/*\[name() != 'category'] | $..book.*\[?(@property !== "category")] | Grabs all children of "book" except for "category" ones | @property is not present in the original spec
-//book/*\[position() != 0] | $..book\[?(@property !== 0)] | Grabs all books whose property (which, being that we are reaching inside an array, is the numeric index) is not 0 | @property is not present in the original spec
-/store/\*/\*\[name(parent::*) != 'book'] | $.store.*\[?(@parentProperty !== "book")] | Grabs the grandchildren of store whose parent property is not book (i.e., bicycle's children, "color" and "price") | @parentProperty is not present in the original spec
-//book\[count(preceding-sibling::\*) != 0]/\*/text() | $..book.*\[?(@parentProperty !== 0)] | Get the property values of all book instances whereby the parent property of these values (i.e., the array index holding the book item parent object) is not 0 | @parentProperty is not present in the original spec
-//book\[price = /store/book\[3]/price] | $..book\[?(@.price === @root.store.book\[2].price)] | Filter all books whose price equals the price of the third book | @root is not present in the original spec
-//book/../\*\[. instance of element(\*, xs:decimal)\] (in XPath 2.0) | $..book..\*@number() | Get the numeric values within the book array | @number(), the other basic types (@boolean(), @string()), other low-level derived types (@null(), @object(), @array()), the JSONSchema-added type, @integer(), the compound type @scalar() (which also accepts `undefined` and non-finite numbers for JavaScript objects as well as all of the basic non-object/non-function types), the type, @other(), to be used in conjunction with a user-defined callback (see `otherTypeCallback`) and the following non-JSON types that can nevertheless be used with JSONPath when querying non-JSON JavaScript objects (@undefined(), @function(), @nonFinite()) are not present in the original spec
-| | `` ` `` (e.g., `` `$`` to match a property literally named `$`) | Escapes the entire sequence following (to be treated as a literal) | `` ` `` is not present in the original spec; to get a literal backtick, use an additional backtick to escape
+comparisons or to prevent ambiguity). Note: to test the XPath examples
+(including 2.0 ones), [this demo](http://videlibri.sourceforge.net/cgi-bin/xidelcgi)
+may be helpful (set to `xml` or `xml-strict`).
+
+| XPath | JSONPath | Result | Notes |
+|-------------------------------------------------------------------------------------|---------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `/store/book/author` | `$.store.book[*].author` | The authors of all books in the store | Can also be represented without the `$.` as `store.book[*].author` (though this is not present in the original spec); note that some character literals (`$` and `@`) require escaping, however |
+| `//author` | `$..author` | All authors | |
+| `/store/*` | `$.store.*` | All things in store, which are its books (a book array) and a red bicycle (a bicycle object). | |
+| `/store//price` | `$.store..price` | The price of everything in the store. | |
+| `//book[3]` | `$..book[2]` | The third book (book object) | |
+| `//book[last()]` | `$..book[(@.length-1)]` `$..book[-1:]` | The last book in order. | To access a property with a special character, utilize `[(@['...'])]` for the filter (this particular feature is not present in the original spec) |
+| `//book[position()<3]` | `$..book[0,1]` `$..book[:2]` | The first two books | |
+| `//book/*[self::category\|self::author]` or `//book/(category,author)` in XPath 2.0 | `$..book[0][category,author]` | The categories and authors of all books | |
+| `//book[isbn]` | `$..book[?(@.isbn)]` | Filter all books with an ISBN number | To access a property with a special character, utilize `[?@['...']]` for the filter (this particular feature is not present in the original spec) |
+| `//book[price<10]` | `$..book[?(@.price<10)]` | Filter all books cheaper than 10 | |
+| `//*[name() = 'price' and . != 8.95]` | `$..*[?(@property === 'price' && @ !== 8.95)]` | Obtain all property values of objects whose property is price and which does not equal 8.95 | With the bare `@` allowing filtering objects by property value (not necessarily within arrays), you can add `^` after the expression to get at the object possessing the filtered properties |
+| `/` | `$` | The root of the JSON object (i.e., the whole object itself) | To get a literal `$` (by itself or anywhere in the path), you must use the backtick escape |
+| `//*/*\|//*/*/text()` | `$..*` | All Elements (and text) beneath root in an XML document. All members of a JSON structure beneath the root. | |
+| `//*` | `$..` | All Elements in an XML document. All parent components of a JSON structure including root. | This behavior was not directly specified in the original spec |
+| `//*[price>19]/..` | `$..[?(@.price>19)]^` | Parent of those specific items with a price greater than 19 (i.e., the store value as the parent of the bicycle and the book array as parent of an individual book) | Parent (caret) not present in the original spec |
+| `/store/*/name()` (in XPath 2.0) | `$.store.*~` | The property names of the store sub-object ("book" and "bicycle"). Useful with wildcard properties. | Property name (tilde) is not present in the original spec |
+| `/store/book[not(. is /store/book[1])]` (in XPath 2.0) | `$.store.book[?(@path !== "$['store']['book'][0]")]` | All books besides that at the path pointing to the first | `@path` is not present in the original spec |
+| `//book[parent::*/bicycle/color = "red"]/category` | `$..book[?(@parent.bicycle && @parent.bicycle.color === "red")].category` | Grabs all categories of books where the parent object of the book has a bicycle child whose color is red (i.e., all the books) | `@parent` is not present in the original spec |
+| `//book/*[name() != 'category']` | `$..book.*[?(@property !== "category")]` | Grabs all children of "book" except for "category" ones | `@property` is not present in the original spec |
+| `//book[position() != 1]` | `$..book[?(@property !== 0)]` | Grabs all books whose property (which, being that we are reaching inside an array, is the numeric index) is not 0 | `@property` is not present in the original spec |
+| `/store/*/*[name(parent::*) != 'book']` | `$.store.*[?(@parentProperty !== "book")]` | Grabs the grandchildren of store whose parent property is not book (i.e., bicycle's children, "color" and "price") | `@parentProperty` is not present in the original spec |
+| `//book[count(preceding-sibling::*) != 0]/*/text()` | `$..book.*[?(@parentProperty !== 0)]` | Get the property values of all book instances whereby the parent property of these values (i.e., the array index holding the book item parent object) is not 0 | `@parentProperty` is not present in the original spec |
+| `//book[price = /store/book[3]/price]` | `$..book[?(@.price === @root.store.book[2].price)]` | Filter all books whose price equals the price of the third book | `@root` is not present in the original spec |
+| `//book/../*[. instance of element(*, xs:decimal)]` (in XPath 2.0) | `$..book..*@number()` | Get the numeric values within the book array | `@number()`, the other basic types (`@boolean()`, `@string()`), other low-level derived types (`@null()`, `@object()`, `@array()`), the JSONSchema-added type, `@integer()`, the compound type `@scalar()` (which also accepts `undefined` and non-finite numbers for JavaScript objects as well as all of the basic non-object/non-function types), the type, `@other()`, to be used in conjunction with a user-defined callback (see `otherTypeCallback`) and the following non-JSON types that can nevertheless be used with JSONPath when querying non-JSON JavaScript objects (`@undefined()`, `@function()`, `@nonFinite()`) are not present in the original spec |
+| `//book/*[name() = 'category' and matches(., 'tion$')]` (XPath 2.0) | `$..book.*[?(@property === "category" && @.match(/TION$/i))]` | All categories of books which match the regex (end in 'TION' case insensitive) | `@property` is not present in the original spec. |
+| `//book/*[matches(name(), 'bn$')]/parent::*` (XPath 2.0) | `$..book.*[?(@property.match(/bn$/i))]^` | All books which have a property matching the regex (end in 'TION' case insensitive) | `@property` is not present in the original spec. Note: Uses the parent selector `^` at the end of the expression to return to the parent object; without the parent selector, it matches the two `isbn` key values. |
+| | `` ` `` (e.g., `` `$`` to match a property literally named `$`) | Escapes the entire sequence following (to be treated as a literal) | `` ` `` is not present in the original spec; to get a literal backtick, use an additional backtick to escape |
Any additional variables supplied as properties on the optional "sandbox"
object option are also available to (parenthetical-based)
@@ -329,6 +373,10 @@ from 0), whereas in XPath, they are 1-based.
1. In JSONPath, equality tests utilize (as per JavaScript) multiple equal signs
whereas in XPath, they use a single equal sign.
+## Command line interface
+
+A basic command line interface (CLI) is provided. Access it using `npx jsonpath-plus `.
+
## Ideas
1. Support OR outside of filters (as in XPath `|`) and grouping.
@@ -354,6 +402,11 @@ npm run browser-test
- Visit [http://localhost:8082/test/](http://localhost:8082/test/).
+
+## Security
+
+Please see [SECURITY.md](./SECURITY.md) for important security considerations and instructions on how to report vulnerabilities.
+
## License
-[MIT License](http://www.opensource.org/licenses/mit-license.php).
+[MIT License](https://opensource.org/license/mit/).
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..01f09f62
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+## Security Considerations
+
+### Query Injection
+
+JSONPath-Plus evaluates JSONPath expressions provided by the caller. While the default `"eval": "safe"` option prevents arbitrary code execution, it **cannot prevent data exposure if the JSONPath query itself is compromised**.
+
+If untrusted input is incorporated into a JSONPath expression, an attacker may be able to alter the query structure by adding additional patterns. This can change how the remaining query is interpreted and may result in **unexpected or broader data being returned** than intended.
+
+**Important notes:**
+- This does **not** enable random code execution when using `"eval": "safe"` (the default).
+- The primary risk is **data leakage**, not execution of attacker-controlled code.
+
+**Mitigations:**
+1. **Do not interpolate unsanitized user input into JSONPath queries.**
+2. If user-controlled input must be included in a query, ensure the target JSON object contains **only non-confidential data**.
+
+As a general rule, treat JSONPath expressions as code and avoid constructing them dynamically from untrusted sources.
+
+## Reporting a Vulnerability
+
+**Please do not report security vulnerabilities through public GitHub issues.**
+
+If you believe you’ve found a security vulnerability, please send it to us by emailing [iamavinashthakur.at@gmail.com](mailto:iamavinashthakur.at@gmail.com) or [brettz9@yahoo.com](mailto:brettz9@yahoo.com). Please include the following details with your report:
+
+1. Description of the location and potential impact of the vulnerability
+
+2. A detailed description of the steps required to reproduce the vulnerability (POC scripts, etc.).
+
+3. How you would like to be credited.
+
+We will evaluate the vulnerability and, if necessary, release a fix or unertake mitigating steps to address it. We will contact you to let you know the outcome, and will credit you in the report.
+
+Please **do not disclose the vulnerability publicly** until we have sufficient time to release a fix.
+
+Once we have either a) published a fix, b) declined to address the vulnerability for whatever reason, or c) taken more than 30 days to reply, we welcome you to publicly report the vulnerability on our tracker and disclose it publicly. If you intend to
+disclose sooner regardless of our requested policy, please at least indicate to us when you plan to disclose.
diff --git a/badges/coverage-badge.svg b/badges/coverage-badge.svg
new file mode 100644
index 00000000..9e7204d2
--- /dev/null
+++ b/badges/coverage-badge.svg
@@ -0,0 +1 @@
+
diff --git a/badges/licenses-badge-dev.svg b/badges/licenses-badge-dev.svg
new file mode 100644
index 00000000..c863d462
--- /dev/null
+++ b/badges/licenses-badge-dev.svg
@@ -0,0 +1 @@
+
diff --git a/badges/licenses-badge.svg b/badges/licenses-badge.svg
new file mode 100644
index 00000000..afd4382d
--- /dev/null
+++ b/badges/licenses-badge.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/badges/tests-badge.svg b/badges/tests-badge.svg
new file mode 100644
index 00000000..a1036d00
--- /dev/null
+++ b/badges/tests-badge.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/bin/jsonpath-cli.js b/bin/jsonpath-cli.js
new file mode 100755
index 00000000..58fbb4bc
--- /dev/null
+++ b/bin/jsonpath-cli.js
@@ -0,0 +1,36 @@
+#!/usr/bin/env node
+import {readFile} from 'fs/promises';
+import {JSONPath as jsonpath} from '../dist/index-node-esm.js';
+
+const file = process.argv[2];
+const path = process.argv[3];
+
+try {
+ const json = JSON.parse(await readFile(file, 'utf8'));
+ runQuery(json, path);
+} catch (e) {
+ /* eslint-disable no-console -- CLI */
+ console.error(`usage: ${process.argv[1]} \n`);
+ console.error(e);
+ /* eslint-enable no-console -- CLI */
+ process.exit(1);
+}
+
+/**
+ * @typedef {any} JSON
+ */
+
+/**
+ * @param {JSON} json
+ * @param {string} pth
+ * @returns {void}
+ */
+function runQuery (json, pth) {
+ const result = jsonpath({
+ json,
+ path: pth
+ });
+
+ // eslint-disable-next-line no-console -- CLI
+ console.log(result);
+}
diff --git a/demo/index.css b/demo/index.css
index 591beebd..0f43aa5e 100644
--- a/demo/index.css
+++ b/demo/index.css
@@ -1,8 +1,17 @@
-#jsonpath {
- width: 90%;
+.row {
+ display: flex;
+ gap: 8px;
margin-bottom: 10px;
}
+.grow, #jsonpath {
+ flex-grow: 1;
+}
+
+.row label {
+ display: flex;
+}
+
.container {
float: left;
width: 48%;
diff --git a/demo/index.html b/demo/index.html
index 7054aadd..40a9dff1 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -9,15 +9,65 @@