Index: query.js
===================================================================
--- query.js	(revision 12658)
+++ query.js	(working copy)
@@ -1,13 +1,25 @@
 dojo.provide("dojox.jsonPath.query");
 
 dojox.jsonPath.query = function(/*Object*/obj, /*String*/expr, /*Object*/arg){
-	// summaRy
+	// summary
 	// 	Perform jsonPath query `expr` on javascript object or json string `obj`
 	//	obj - object || json string to perform query on
 	//	expr - jsonPath expression (string) to be evaluated
 	//	arg - {}special arugments.  
-	//		resultType: "VALUE"||"BOTH"||"PATH"} (defaults to value)
+	//		resultType: "VALUE"||"BOTH"||"PATH"} (defaults to VALUE)
+	//				This indicates whether the result set will be the actual values returned by the JsonPath query
+	// 				or if it will just return results as normalized paths to the value that can later be used to 
+	// 				retrieve the actual values. If both are desired, the "BOTH" option may be used an it will
+	//				return an array of objects with a path property for the path and a value property for the
+	//				value.
 	//		evalType: "RESULT"||"ITEM"} (defaults to ?)
+	//				This indicates whether to use result-based or item-based evaluation. See the README
+	//				for more information.
+	//		safeEval: true || false} (defaults to false)
+	//				This indicates whether to use safe eval for sub-expressions. Using safe-eval prevents
+	// 				the execution of arbitrary code, is recommended for user supplied queries. Safe-eval
+	// 				also uses a subset of operators that ensures portability of the JSONPath expressions 
+	// 				across all languages (instead of just JavaScript). 
 
 	var re = dojox.jsonPath._regularExpressions;
 	if (!arg){arg={};}
@@ -23,8 +35,12 @@
 			expr = expr.replace(/'([^']|'')*'/g, function(t){return "_str("+(strs.push(eval(t))-1)+")";});
 			var ll = -1;
 			while(ll!=subx.length){
-				ll=subx.length;//TODO: Do expression syntax checking
-				expr = expr.replace(/(\??\([^\(\)]*\))/g, function($0){return "#"+(subx.push($0)-1);});
+				ll=subx.length;
+				expr = expr.replace(/(\??\([^\(\)]*\))/g, function($0){
+					if (arg.safeEval && !$0.match(/^\??\(\s*((_str)?#?[\w\]@]+\s*[\.\+\-\/\*\>\=\<\!\|\&\[\?\:]+\s*)*(_str)?#?[\w\]]+\s*\)$/g))
+						throw Error("This expression " + $0 + " is not safely allowed");
+					return "#"+(subx.push($0.replace(/([^=<>!])=([^=])/,"$1===$2"))-1);
+				});
 			}
 			expr = expr.replace(/[\['](#[0-9]+)[\]']/g,'[$1]')
 						  .replace(/'?\.'?|\['?/g, ";")
Index: README
===================================================================
--- README	(revision 12658)
+++ README	(working copy)
@@ -1,125 +1,150 @@
--------------------------------------------------------------------------------
-dojox.jsonPath
--------------------------------------------------------------------------------
-Version 1.0
-Release date: 11/14/2007
--------------------------------------------------------------------------------
-Project state: beta
--------------------------------------------------------------------------------
-Project authors
-	Dustin Machi
-	Kris Zyp
--------------------------------------------------------------------------------
-Project description
-
-jsonPath is a query system similar in idea to xpath, but for use against
-javascript objects.  This code is a port of the jsonPath code at 
-http://code.google.com/p/jsonpath/.  It was contributed under CLA by Stefan
-Goessner.  Thanks Stefan! 
--------------------------------------------------------------------------------
-Dependencies:
-
-Dojo Core (package loader).
--------------------------------------------------------------------------------
-Documentation
-
-Usage:
-
-var matches = dojox.jsonPath.query(objectToQuery, jsonPathExpresson)
-
-Expressions:
-
-	$			The Root Object
-	@			The current object/element
-	. or []			The child operator
-	..			Recursive descent
-	*			all objects
-	[]			subscript operator
-	[,]			Union operator
-	[start:end:step]	array slice operator
-	?()			applies a filter/script expression
-	()			script expresions
-
-	some examples:
-
-	Given the following test data set:
-
-	var json = 
-                  { "store": {
-                        "book": [ 
-                          { "category": "reference",
-                                "author": "Nigel Rees",
-                                "title": "Sayings of the Century",
-                                "price": 8.95
-                          },
-                          { "category": "fiction",
-                                "author": "Evelyn Waugh",
-                                "title": "Sword of Honour",
-                                "price": 12.99
-                          },
-                          { "category": "fiction",
-                                "author": "Herman Melville",
-                                "title": "Moby Dick",
-                                "isbn": "0-553-21311-3",
-                                "price": 8.99
-                          },
-                          { "category": "fiction",
-                                "author": "J. R. R. Tolkien",
-                                "title": "The Lord of the Rings",
-                                "isbn": "0-395-19395-8",
-                                "price": 22.99
-                          }
-                        ],
-                        "bicycle": {
-                          "color": "red",
-                          "price": 19.95
-                        }
-                  }
-                };
-
-	Here are some example queries and their output:
-
-   	$.store.book[*].author 
-	["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]
-
-	$..author
-	["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]
-
-	$.store.*
-	[[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}],{"color":"red","price":19.95}]
-
-	$.store..price
-	[8.95,12.99,8.99,22.99,19.95]
-
-	$..book[(@.length-1)]
-	[{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
-
-	$..book[-1]
-	[{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
-
-	$..book[0,1]
-	[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99}]
-	
-	$..book[:2]
-	[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99}]
-
-	$..book[?(@.isbn)]
-	[{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
-
-	$..book[?(@.price<10)]
-	[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}]
-
-	$..*
-	[{"book":[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}],"bicycle":{"color":"red","price":19.95}},[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}],{"color":"red","price":19.95},{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99},"reference","Nigel Rees","Sayings of the Century",8.95,"fiction","Evelyn Waugh","Sword of Honour",12.99,"fiction","Herman Melville","Moby Dick","0-553-21311-3",8.99,"fiction","J. R. R. Tolkien","The Lord of the Rings","0-395-19395-8",22.99,"red",19.95]
-
-
--------------------------------------------------------------------------------
-Installation instructions
-
-Grab the following from the Dojo SVN Repository:
-http://svn.dojotoolkit.org/var/src/dojo/dojox/trunk/jsonPath
-
-Install into the following directory structure:
-/dojox/jsonPath/
-
-...which should be at the same level as your Dojo checkout.
+-------------------------------------------------------------------------------
+dojox.jsonPath
+-------------------------------------------------------------------------------
+Version 1.0
+Release date: 11/14/2007
+-------------------------------------------------------------------------------
+Project state: beta
+-------------------------------------------------------------------------------
+Project authors
+	Dustin Machi
+	Kris Zyp
+-------------------------------------------------------------------------------
+Project description
+
+jsonPath is a query system similar in idea to xpath, but for use against
+javascript objects.  This code is a port of the jsonPath code at 
+http://code.google.com/p/jsonpath/.  It was contributed under CLA by Stefan
+Goessner.  Thanks Stefan! 
+-------------------------------------------------------------------------------
+Dependencies:
+
+Dojo Core (package loader).
+-------------------------------------------------------------------------------
+Documentation
+
+Usage:
+
+var matches = dojox.jsonPath.query(objectToQuery, jsonPathExpresson, options)
+
+jsonPath supports two forms of evaluation. Item-based evaluation, which is the 
+default evaluation, executes each expression on each item in the result at each step in evaluation.
+Result-based evaluation executes each expression on the result at each step in evaluation, and 
+can be selected by providing the third parameter, options with an evalType equal to "RESULT" like:
+jsonPath(object,query, {evalType:"RESULT"}); 
+Item-based evaluation will always produce an array of results. Result-basd evaluation follows the
+semantics of normal JavaScript more closely. Selecting a single item from an array by index, will actually
+return that item rather than an array with that item in it. With result-based evaluation, you can also
+use the slice operator to narrow down search results, allowing you to request paged results of queries. 
+
+  
+Expressions:
+
+	$			The Root Object
+	@			The current object/element
+	. or []			The child operator
+	..			Recursive descent
+	*			all objects
+	[]			subscript operator
+	[,]			Union operator
+	[start:end:step]	array slice operator
+	?()			applies a filter sub-expression to extract all items from an array that match the expression
+	()			sub-expression
+
+	some examples:
+	
+	For portability, sub-expressions should be limited to the following operators:
+	&, |, =, !=, >=, <=, +, -, /, *, ?, :
+	
+	You may use the safe-eval option {safeEval:true}, to restrict sub-expressions to these operators. This
+	prevents the execution of arbitrary code, and is recommended for user supplied queries. Safe-eval
+	also uses a subset of operators that ensures portability of the JSONPath expressions across all 
+	languages (without requiring a JavaScript interpreter). 
+	
+	Given the following test data set:
+
+	var json = 
+                  { "store": {
+                        "book": [ 
+                          { "category": "reference",
+                                "author": "Nigel Rees",
+                                "title": "Sayings of the Century",
+                                "price": 8.95
+                          },
+                          { "category": "fiction",
+                                "author": "Evelyn Waugh",
+                                "title": "Sword of Honour",
+                                "price": 12.99
+                          },
+                          { "category": "fiction",
+                                "author": "Herman Melville",
+                                "title": "Moby Dick",
+                                "isbn": "0-553-21311-3",
+                                "price": 8.99
+                          },
+                          { "category": "fiction",
+                                "author": "J. R. R. Tolkien",
+                                "title": "The Lord of the Rings",
+                                "isbn": "0-395-19395-8",
+                                "price": 22.99
+                          }
+                        ],
+                        "bicycle": {
+                          "color": "red",
+                          "price": 19.95
+                        }
+                  }
+                };
+
+	Here are some example queries and their output (when result-based 
+	evaluation and item-based evaluation differ, it is noted):
+
+   	$.store.book[*].author 
+	["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]
+
+	$..author
+	["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]
+
+	$.store.*
+	[[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}],{"color":"red","price":19.95}]
+
+	$.store..price
+	[8.95,12.99,8.99,22.99,19.95]
+
+	$..book[(@.length-1)]
+item-based:	[{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
+result-based: {"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}
+
+	$..book[-1]
+item-based:	[{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
+result-based: {"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}
+	
+	$..book[0,1]
+	[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99}]
+	
+	$..book[:2]
+	[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99}]
+
+	$..book[?(@.isbn)]
+	[{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]
+
+	$..book[?(@.price<10)]
+	[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}]
+
+	$..*
+	[{"book":[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}],"bicycle":{"color":"red","price":19.95}},[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}],{"color":"red","price":19.95},{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99},"reference","Nigel Rees","Sayings of the Century",8.95,"fiction","Evelyn Waugh","Sword of Honour",12.99,"fiction","Herman Melville","Moby Dick","0-553-21311-3",8.99,"fiction","J. R. R. Tolkien","The Lord of the Rings","0-395-19395-8",22.99,"red",19.95]
+
+	$..book[?(@.price<10)][0:2]
+item-based: []
+result-based: [{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99}]
+
+-------------------------------------------------------------------------------
+Installation instructions
+
+Grab the following from the Dojo SVN Repository:
+http://svn.dojotoolkit.org/var/src/dojo/dojox/trunk/jsonPath
+
+Install into the following directory structure:
+/dojox/jsonPath/
+
+...which should be at the same level as your Dojo checkout.
Index: tests/jsonPath.js
===================================================================
--- tests/jsonPath.js	(revision 12658)
+++ tests/jsonPath.js	(working copy)
@@ -116,7 +116,7 @@
 		{
 			name: "$..book[?(@.isbn)]",
 			runTest: function(t) {
-				var result = dojo.toJson(dojox.jsonPath.query(dojox.jsonPath.tests.testData, this.name));
+				var result = dojo.toJson(dojox.jsonPath.query(dojox.jsonPath.tests.testData, this.name,{safeEval:true}));
 				var success =  '[{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}]';
 				doh.assertEqual(success,result);
 			}
@@ -124,7 +124,7 @@
 		{
 			name: "$..book[?(@.price<10)]",
 			runTest: function(t) {
-				var result = dojo.toJson(dojox.jsonPath.query(dojox.jsonPath.tests.testData, this.name));
+				var result = dojo.toJson(dojox.jsonPath.query(dojox.jsonPath.tests.testData, this.name,{safeEval:true}));
 				var success =  '[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}]';
 				doh.assertEqual(success,result);
 			}
@@ -132,7 +132,7 @@
 		{
 			name: "$.symbols[*]",
 			runTest: function(t) {
-				var result = dojo.toJson(dojox.jsonPath.query(dojox.jsonPath.tests.testData, this.name));
+				var result = dojo.toJson(dojox.jsonPath.query(dojox.jsonPath.tests.testData, this.name,{safeEval:true}));
 				var success =  '[5]';
 				doh.assertEqual(success,result);
 			}
@@ -148,7 +148,7 @@
 		{
 			name: "$.symbols[(@[('@.$;')]?'@.$;':'@.$;')]",
 			runTest: function(t) {
-				var result = dojo.toJson(dojox.jsonPath.query(dojox.jsonPath.tests.testData, this.name));
+				var result = dojo.toJson(dojox.jsonPath.query(dojox.jsonPath.tests.testData, this.name,{safeEval:true}));
 				var success =  '[5]';
 				doh.assertEqual(success,result);
 			}
@@ -201,6 +201,42 @@
 				doh.assertEqual(success,result);
 			}
 		},
+		{
+			name: "safeEval: Illegal Eval",
+			runTest: function(t) {
+				try { 
+					var result = dojo.toJson(dojox.jsonPath.query(dojox.jsonPath.tests.testData, "$.store.book[?(dangerousCall(5))]",{evalType:"RESULT", safeEval:true}));
+					console.log("Illegal eval permitted");
+					doh.e("Illegal eval was permitted");
+				} catch(e) {
+					console.log("Eval properly blocked", e);	
+				}
+			}
+		},
+		{
+			name: "safeEval: Illegal Eval 2",
+			runTest: function(t) {
+				try { 
+					var result = dojo.toJson(dojox.jsonPath.query(dojox.jsonPath.tests.testData, "$.store.book[?(new Danger)]",{evalType:"RESULT", safeEval:true}));
+					console.log("Illegal eval permitted");
+					doh.e("Illegal eval was permitted");
+				} catch(e) {
+					console.log("Eval properly blocked", e);						
+				}
+			}
+		},
+		{
+			name: "safeEval: Illegal Eval 3",
+			runTest: function(t) {
+				try { 
+					var result = dojo.toJson(dojox.jsonPath.query(dojox.jsonPath.tests.testData, "$.store.book[?(@+=2)]",{evalType:"RESULT", safeEval:true}));
+					console.log("Illegal eval permitted");
+					doh.e("Illegal eval was permitted");
+				} catch(e) {
+					console.log("Eval properly blocked", e);	
+				}
+			}
+		}
 
 	]
 );
