aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/etherpad/src/etherpad/collab/json_sans_eval.js
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/etherpad/src/etherpad/collab/json_sans_eval.js')
-rw-r--r--trunk/etherpad/src/etherpad/collab/json_sans_eval.js178
1 files changed, 178 insertions, 0 deletions
diff --git a/trunk/etherpad/src/etherpad/collab/json_sans_eval.js b/trunk/etherpad/src/etherpad/collab/json_sans_eval.js
new file mode 100644
index 0000000..6cbd497
--- /dev/null
+++ b/trunk/etherpad/src/etherpad/collab/json_sans_eval.js
@@ -0,0 +1,178 @@
+// Copyright (C) 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Parses a string of well-formed JSON text.
+ *
+ * If the input is not well-formed, then behavior is undefined, but it is
+ * deterministic and is guaranteed not to modify any object other than its
+ * return value.
+ *
+ * This does not use `eval` so is less likely to have obscure security bugs than
+ * json2.js.
+ * It is optimized for speed, so is much faster than json_parse.js.
+ *
+ * This library should be used whenever security is a concern (when JSON may
+ * come from an untrusted source), speed is a concern, and erroring on malformed
+ * JSON is *not* a concern.
+ *
+ * Pros Cons
+ * +-----------------------+-----------------------+
+ * json_sans_eval.js | Fast, secure | Not validating |
+ * +-----------------------+-----------------------+
+ * json_parse.js | Validating, secure | Slow |
+ * +-----------------------+-----------------------+
+ * json2.js | Fast, some validation | Potentially insecure |
+ * +-----------------------+-----------------------+
+ *
+ * json2.js is very fast, but potentially insecure since it calls `eval` to
+ * parse JSON data, so an attacker might be able to supply strange JS that
+ * looks like JSON, but that executes arbitrary javascript.
+ * If you do have to use json2.js with untrusted data, make sure you keep
+ * your version of json2.js up to date so that you get patches as they're
+ * released.
+ *
+ * @param {string} json per RFC 4627
+ * @return {Object|Array}
+ * @author Mike Samuel <mikesamuel@gmail.com>
+ */
+var jsonParse = (function () {
+ var number
+ = '(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)';
+ var oneChar = '(?:[^\\0-\\x08\\x0a-\\x1f\"\\\\]'
+ + '|\\\\(?:[\"/\\\\bfnrt]|u[0-9A-Fa-f]{4}|x7c))';
+ var string = '(?:\"' + oneChar + '*\")';
+
+ // Will match a value in a well-formed JSON file.
+ // If the input is not well-formed, may match strangely, but not in an unsafe
+ // way.
+ // Since this only matches value tokens, it does not match whitespace, colons,
+ // or commas.
+ var jsonToken = new RegExp(
+ '(?:false|true|null|[\\{\\}\\[\\]]'
+ + '|' + number
+ + '|' + string
+ + ')', 'g');
+
+ // Matches escape sequences in a string literal
+ var escapeSequence = new RegExp('\\\\(?:([^ux]|x7c)|u(.{4}))', 'g');
+
+ // Decodes escape sequences in object literals
+ var escapes = {
+ '"': '"',
+ '/': '/',
+ '\\': '\\',
+ 'b': '\b',
+ 'f': '\f',
+ 'n': '\n',
+ 'r': '\r',
+ 't': '\t',
+ 'x7c': '|'
+ };
+ function unescapeOne(_, ch, hex) {
+ return ch ? escapes[ch] : String.fromCharCode(parseInt(hex, 16));
+ }
+
+ // A non-falsy value that coerces to the empty string when used as a key.
+ var EMPTY_STRING = new String('');
+ var SLASH = '\\';
+
+ // Constructor to use based on an open token.
+ var firstTokenCtors = { '{': Object, '[': Array };
+
+ return function (json) {
+ // Split into tokens
+ var toks = json.match(jsonToken);
+ // Construct the object to return
+ var result;
+ var tok = toks[0];
+ if ('{' === tok) {
+ result = {};
+ } else if ('[' === tok) {
+ result = [];
+ } else {
+ throw new Error(tok);
+ }
+
+ // If undefined, the key in an object key/value record to use for the next
+ // value parsed.
+ var key;
+ // Loop over remaining tokens maintaining a stack of uncompleted objects and
+ // arrays.
+ var stack = [result];
+ for (var i = 1, n = toks.length; i < n; ++i) {
+ tok = toks[i];
+
+ var cont;
+ switch (tok.charCodeAt(0)) {
+ default: // sign or digit
+ cont = stack[0];
+ cont[key || cont.length] = +(tok);
+ key = void 0;
+ break;
+ case 0x22: // '"'
+ tok = tok.substring(1, tok.length - 1);
+ if (tok.indexOf(SLASH) !== -1) {
+ tok = tok.replace(escapeSequence, unescapeOne);
+ }
+ cont = stack[0];
+ if (!key) {
+ if (cont instanceof Array) {
+ key = cont.length;
+ } else {
+ key = tok || EMPTY_STRING; // Use as key for next value seen.
+ break;
+ }
+ }
+ cont[key] = tok;
+ key = void 0;
+ break;
+ case 0x5b: // '['
+ cont = stack[0];
+ stack.unshift(cont[key || cont.length] = []);
+ key = void 0;
+ break;
+ case 0x5d: // ']'
+ stack.shift();
+ break;
+ case 0x66: // 'f'
+ cont = stack[0];
+ cont[key || cont.length] = false;
+ key = void 0;
+ break;
+ case 0x6e: // 'n'
+ cont = stack[0];
+ cont[key || cont.length] = null;
+ key = void 0;
+ break;
+ case 0x74: // 't'
+ cont = stack[0];
+ cont[key || cont.length] = true;
+ key = void 0;
+ break;
+ case 0x7b: // '{'
+ cont = stack[0];
+ stack.unshift(cont[key || cont.length] = {});
+ key = void 0;
+ break;
+ case 0x7d: // '}'
+ stack.shift();
+ break;
+ }
+ }
+ // Fail if we've got an uncompleted object.
+ if (stack.length) { throw new Error(); }
+ return result;
+ };
+})();