pax_global_header00006660000000000000000000000064132443752550014524gustar00rootroot0000000000000052 comment=8f193cae10a2208010102fd50f0b61e869e14dcb sqlstring-2.3.1/000077500000000000000000000000001324437525500135555ustar00rootroot00000000000000sqlstring-2.3.1/.editorconfig000066400000000000000000000002631324437525500162330ustar00rootroot00000000000000# http://editorconfig.org root = true [*] charset = utf-8 insert_final_newline = true trim_trailing_whitespace = true [{*.js,*.json,*.yml}] indent_size = 2 indent_style = space sqlstring-2.3.1/.eslintignore000066400000000000000000000000271324437525500162570ustar00rootroot00000000000000.nyc_output/ coverage/ sqlstring-2.3.1/.eslintrc000066400000000000000000000025111324437525500154000ustar00rootroot00000000000000{ "env": { "node": true }, "rules": { "comma-dangle": [2, "never"], "comma-spacing": ["error", { "before": false, "after": true }], "consistent-return": 2, "eqeqeq": [2, "allow-null"], "indent": [2, 2, { "VariableDeclarator": 2, "SwitchCase": 1 }], "key-spacing": [2, { "align": { "beforeColon": true, "afterColon": true, "on": "colon" } }], "keyword-spacing": 2, "new-parens": 2, "no-cond-assign": 2, "no-constant-condition": 2, "no-control-regex": 2, "no-debugger": 2, "no-dupe-args": 2, "no-dupe-keys": 2, "no-duplicate-case": 2, "no-empty-character-class": 2, "no-empty": 2, "no-ex-assign": 2, "no-extra-boolean-cast": 2, "no-extra-semi": 2, "no-func-assign": 2, "no-inner-declarations": 2, "no-invalid-regexp": 2, "no-irregular-whitespace": 2, "no-multiple-empty-lines": [2, { "max": 1 }], "no-negated-in-lhs": 2, "no-obj-calls": 2, "no-regex-spaces": 2, "no-sparse-arrays": 2, "no-trailing-spaces": 2, "no-unexpected-multiline": 2, "no-unreachable": 2, "no-unused-vars": 2, "quotes": [2, "single", { "avoidEscape": true, "allowTemplateLiterals": true }], "semi": [2, "always"], "semi-spacing": 2, "space-infix-ops": 2, "use-isnan": 2, "valid-jsdoc": 2, "valid-typeof": 2 } } sqlstring-2.3.1/.gitignore000066400000000000000000000001021324437525500155360ustar00rootroot00000000000000.nyc_output coverage node_modules npm-debug.log package-lock.json sqlstring-2.3.1/.travis.yml000066400000000000000000000022141324437525500156650ustar00rootroot00000000000000language: node_js node_js: - "0.6" - "0.8" - "0.10" - "0.12" - "1.8" - "2.5" - "3.3" - "4.8" - "5.12" - "6.13" - "7.10" - "8.9" - "9.6" sudo: false dist: precise cache: directories: - node_modules before_install: # Skip updating shrinkwrap / lock - "npm config set shrinkwrap false" # Remove all non-test dependencies - "npm rm --save-dev benchmark beautify-benchmark" # Setup Node.js version-specific dependencies - "test $TRAVIS_NODE_VERSION != '0.6' || npm rm --save-dev nyc" - "test $TRAVIS_NODE_VERSION != '0.8' || npm rm --save-dev nyc" - "test $(echo $TRAVIS_NODE_VERSION | cut -d'.' -f1) -ge 4 || npm rm --save-dev eslint eslint-plugin-markdown" # Update Node.js modules - "test ! -d node_modules || npm prune" - "test ! -d node_modules || npm rebuild" script: # Run test script, depending on nyc install - "test ! -z $(npm -ps ls nyc) || npm test" - "test -z $(npm -ps ls nyc) || npm run-script test-ci" # Run linter - "test -z $(npm -ps ls eslint) || npm run-script lint" after_script: - "test -d .nyc_output && npm install coveralls@2 && nyc report --reporter=text-lcov | coveralls" sqlstring-2.3.1/HISTORY.md000066400000000000000000000015331324437525500152420ustar00rootroot000000000000002.3.1 / 2018-02-24 ================== * Fix incorrectly replacing non-placeholders in SQL 2.3.0 / 2017-10-01 ================== * Add `.toSqlString()` escape overriding * Add `raw` method to wrap raw strings for escape overriding * Small performance improvement on `escapeId` 2.2.0 / 2016-11-01 ================== * Escape invalid `Date` objects as `NULL` 2.1.0 / 2016-09-26 ================== * Accept numbers and other value types in `escapeId` * Run `buffer.toString()` through escaping 2.0.1 / 2016-06-06 ================== * Fix npm package to include missing `lib/` directory 2.0.0 / 2016-06-06 ================== * Bring repository up-to-date with `mysql` module changes * Support Node.js 0.6.x 1.0.0 / 2014-11-09 ================== * Support Node.js 0.8.x 0.0.1 / 2014-02-25 ================== * Initial release sqlstring-2.3.1/LICENSE000066400000000000000000000021361324437525500145640ustar00rootroot00000000000000Copyright (c) 2012 Felix Geisendörfer (felix@debuggable.com) and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. sqlstring-2.3.1/README.md000066400000000000000000000174411324437525500150430ustar00rootroot00000000000000# sqlstring [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Node.js Version][node-image]][node-url] [![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] Simple SQL escape and format for MySQL ## Install ```sh $ npm install sqlstring ``` ## Usage ```js var SqlString = require('sqlstring'); ``` ### Escaping query values **Caution** These methods of escaping values only works when the [NO_BACKSLASH_ESCAPES](https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_no_backslash_escapes) SQL mode is disabled (which is the default state for MySQL servers). In order to avoid SQL Injection attacks, you should always escape any user provided data before using it inside a SQL query. You can do so using the `SqlString.escape()` method: ```js var userId = 'some user provided value'; var sql = 'SELECT * FROM users WHERE id = ' + SqlString.escape(userId); console.log(sql); // SELECT * FROM users WHERE id = 'some user provided value' ``` Alternatively, you can use `?` characters as placeholders for values you would like to have escaped like this: ```js var userId = 1; var sql = SqlString.format('SELECT * FROM users WHERE id = ?', [userId]); console.log(sql); // SELECT * FROM users WHERE id = 1 ``` Multiple placeholders are mapped to values in the same order as passed. For example, in the following query `foo` equals `a`, `bar` equals `b`, `baz` equals `c`, and `id` will be `userId`: ```js var userId = 1; var sql = SqlString.format('UPDATE users SET foo = ?, bar = ?, baz = ? WHERE id = ?', ['a', 'b', 'c', userId]); console.log(sql); // UPDATE users SET foo = 'a', bar = 'b', baz = 'c' WHERE id = 1 ``` This looks similar to prepared statements in MySQL, however it really just uses the same `SqlString.escape()` method internally. **Caution** This also differs from prepared statements in that all `?` are replaced, even those contained in comments and strings. Different value types are escaped differently, here is how: * Numbers are left untouched * Booleans are converted to `true` / `false` * Date objects are converted to `'YYYY-mm-dd HH:ii:ss'` strings * Buffers are converted to hex strings, e.g. `X'0fa5'` * Strings are safely escaped * Arrays are turned into list, e.g. `['a', 'b']` turns into `'a', 'b'` * Nested arrays are turned into grouped lists (for bulk inserts), e.g. `[['a', 'b'], ['c', 'd']]` turns into `('a', 'b'), ('c', 'd')` * Objects that have a `toSqlString` method will have `.toSqlString()` called and the returned value is used as the raw SQL. * Objects are turned into `key = 'val'` pairs for each enumerable property on the object. If the property's value is a function, it is skipped; if the property's value is an object, toString() is called on it and the returned value is used. * `undefined` / `null` are converted to `NULL` * `NaN` / `Infinity` are left as-is. MySQL does not support these, and trying to insert them as values will trigger MySQL errors until they implement support. You may have noticed that this escaping allows you to do neat things like this: ```js var post = {id: 1, title: 'Hello MySQL'}; var sql = SqlString.format('INSERT INTO posts SET ?', post); console.log(sql); // INSERT INTO posts SET `id` = 1, `title` = 'Hello MySQL' ``` And the `toSqlString` method allows you to form complex queries with functions: ```js var CURRENT_TIMESTAMP = { toSqlString: function() { return 'CURRENT_TIMESTAMP()'; } }; var sql = SqlString.format('UPDATE posts SET modified = ? WHERE id = ?', [CURRENT_TIMESTAMP, 42]); console.log(sql); // UPDATE posts SET modified = CURRENT_TIMESTAMP() WHERE id = 42 ``` To generate objects with a `toSqlString` method, the `SqlString.raw()` method can be used. This creates an object that will be left un-touched when using in a `?` placeholder, useful for using functions as dynamic values: **Caution** The string provided to `SqlString.raw()` will skip all escaping functions when used, so be careful when passing in unvalidated input. ```js var CURRENT_TIMESTAMP = SqlString.raw('CURRENT_TIMESTAMP()'); var sql = SqlString.format('UPDATE posts SET modified = ? WHERE id = ?', [CURRENT_TIMESTAMP, 42]); console.log(sql); // UPDATE posts SET modified = CURRENT_TIMESTAMP() WHERE id = 42 ``` If you feel the need to escape queries by yourself, you can also use the escaping function directly: ```js var sql = 'SELECT * FROM posts WHERE title=' + SqlString.escape('Hello MySQL'); console.log(sql); // SELECT * FROM posts WHERE title='Hello MySQL' ``` ### Escaping query identifiers If you can't trust an SQL identifier (database / table / column name) because it is provided by a user, you should escape it with `SqlString.escapeId(identifier)` like this: ```js var sorter = 'date'; var sql = 'SELECT * FROM posts ORDER BY ' + SqlString.escapeId(sorter); console.log(sql); // SELECT * FROM posts ORDER BY `date` ``` It also supports adding qualified identifiers. It will escape both parts. ```js var sorter = 'date'; var sql = 'SELECT * FROM posts ORDER BY ' + SqlString.escapeId('posts.' + sorter); console.log(sql); // SELECT * FROM posts ORDER BY `posts`.`date` ``` If you do not want to treat `.` as qualified identifiers, you can set the second argument to `true` in order to keep the string as a literal identifier: ```js var sorter = 'date.2'; var sql = 'SELECT * FROM posts ORDER BY ' + SqlString.escapeId(sorter, true); console.log(sql); // SELECT * FROM posts ORDER BY `date.2` ``` Alternatively, you can use `??` characters as placeholders for identifiers you would like to have escaped like this: ```js var userId = 1; var columns = ['username', 'email']; var sql = SqlString.format('SELECT ?? FROM ?? WHERE id = ?', [columns, 'users', userId]); console.log(sql); // SELECT `username`, `email` FROM `users` WHERE id = 1 ``` **Please note that this last character sequence is experimental and syntax might change** When you pass an Object to `.escape()` or `.format()`, `.escapeId()` is used to avoid SQL injection in object keys. ### Formatting queries You can use `SqlString.format` to prepare a query with multiple insertion points, utilizing the proper escaping for ids and values. A simple example of this follows: ```js var userId = 1; var inserts = ['users', 'id', userId]; var sql = SqlString.format('SELECT * FROM ?? WHERE ?? = ?', inserts); console.log(sql); // SELECT * FROM `users` WHERE `id` = 1 ``` Following this you then have a valid, escaped query that you can then send to the database safely. This is useful if you are looking to prepare the query before actually sending it to the database. You also have the option (but are not required) to pass in `stringifyObject` and `timeZone`, allowing you provide a custom means of turning objects into strings, as well as a location-specific/timezone-aware `Date`. This can be further combined with the `SqlString.raw()` helper to generate SQL that includes MySQL functions as dynamic vales: ```js var userId = 1; var data = { email: 'foobar@example.com', modified: SqlString.raw('NOW()') }; var sql = SqlString.format('UPDATE ?? SET ? WHERE `id` = ?', ['users', data, userId]); console.log(sql); // UPDATE `users` SET `email` = 'foobar@example.com', `modified` = NOW() WHERE `id` = 1 ``` ## License [MIT](LICENSE) [npm-version-image]: https://img.shields.io/npm/v/sqlstring.svg [npm-downloads-image]: https://img.shields.io/npm/dm/sqlstring.svg [npm-url]: https://npmjs.org/package/sqlstring [travis-image]: https://img.shields.io/travis/mysqljs/sqlstring/master.svg [travis-url]: https://travis-ci.org/mysqljs/sqlstring [coveralls-image]: https://img.shields.io/coveralls/mysqljs/sqlstring/master.svg [coveralls-url]: https://coveralls.io/r/mysqljs/sqlstring?branch=master [node-image]: https://img.shields.io/node/v/sqlstring.svg [node-url]: https://nodejs.org/en/download sqlstring-2.3.1/benchmark/000077500000000000000000000000001324437525500155075ustar00rootroot00000000000000sqlstring-2.3.1/benchmark/escape.js000066400000000000000000000030171324437525500173060ustar00rootroot00000000000000var benchmark = require('benchmark'); var benchmarks = require('beautify-benchmark'); global.SqlString = require('..'); global.arr = [ 42, 'foobar' ]; global.buf = new Buffer('foobar'); global.date = new Date(0); global.func = { toSqlString: function () { return 'NOW()'; } }; global.num = 42; global.obj = { foo: 'bar' }; global.str = 'foobar'; var suite = new benchmark.Suite(); suite.add({ name : 'array', minSamples : 100, fn : 'var val = SqlString.escape(arr);' }); suite.add({ name : 'boolean', minSamples : 100, fn : 'var val = SqlString.escape(true);' }); suite.add({ name : 'date', minSamples : 100, fn : 'var val = SqlString.escape(date);' }); suite.add({ name : 'function', minSamples : 100, fn : 'var val = SqlString.escape(func);' }); suite.add({ name : 'null', minSamples : 100, fn : 'var val = SqlString.escape(null);' }); suite.add({ name : 'number', minSamples : 100, fn : 'var val = SqlString.escape(num);' }); suite.add({ name : 'object', minSamples : 100, fn : 'var val = SqlString.escape(obj);' }); suite.add({ name : 'string', minSamples : 100, fn : 'var val = SqlString.escape(str);' }); suite.on('start', function onCycle() { process.stdout.write(' escape\n\n'); }); suite.on('cycle', function onCycle(event) { benchmarks.add(event.target); }); suite.on('complete', function onComplete() { benchmarks.log(); }); suite.run({async: false}); sqlstring-2.3.1/benchmark/escapeId.js000066400000000000000000000014361324437525500175660ustar00rootroot00000000000000var benchmark = require('benchmark'); var benchmarks = require('beautify-benchmark'); global.SqlString = require('..'); var suite = new benchmark.Suite(); suite.add({ name : '"col"', minSamples : 100, fn : 'var val = SqlString.escapeId("col");' }); suite.add({ name : '"tbl.col"', minSamples : 100, fn : 'var val = SqlString.escapeId("tbl.col");' }); suite.add({ name : '["col1", "col2"]', minSamples : 100, fn : 'var val = SqlString.escapeId(["col1", "col2"]);' }); suite.on('start', function onCycle() { process.stdout.write(' escapeId\n\n'); }); suite.on('cycle', function onCycle(event) { benchmarks.add(event.target); }); suite.on('complete', function onComplete() { benchmarks.log(); }); suite.run({async: false}); sqlstring-2.3.1/benchmark/index.js000066400000000000000000000014111324437525500171510ustar00rootroot00000000000000var fs = require('fs'); var path = require('path'); var spawn = require('child_process').spawn; var exe = process.argv[0]; var cwd = process.cwd(); for (var dep in process.versions) { console.log(' %s@%s', dep, process.versions[dep]); } console.log(''); runScripts(fs.readdirSync(__dirname)); function runScripts(fileNames) { var fileName = fileNames.shift(); if (!fileName) { return; } if (!/\.js$/i.test(fileName) || fileName.toLowerCase() === 'index.js') { runScripts(fileNames); return; } var fullPath = path.join(__dirname, fileName); console.log('> %s %s', exe, path.relative(cwd, fullPath)); var proc = spawn(exe, [fullPath], { 'stdio': 'inherit' }); proc.on('exit', function () { runScripts(fileNames); }); } sqlstring-2.3.1/index.js000066400000000000000000000000551324437525500152220ustar00rootroot00000000000000module.exports = require('./lib/SqlString'); sqlstring-2.3.1/lib/000077500000000000000000000000001324437525500143235ustar00rootroot00000000000000sqlstring-2.3.1/lib/SqlString.js000066400000000000000000000133421324437525500166120ustar00rootroot00000000000000var SqlString = exports; var ID_GLOBAL_REGEXP = /`/g; var QUAL_GLOBAL_REGEXP = /\./g; var CHARS_GLOBAL_REGEXP = /[\0\b\t\n\r\x1a\"\'\\]/g; // eslint-disable-line no-control-regex var CHARS_ESCAPE_MAP = { '\0' : '\\0', '\b' : '\\b', '\t' : '\\t', '\n' : '\\n', '\r' : '\\r', '\x1a' : '\\Z', '"' : '\\"', '\'' : '\\\'', '\\' : '\\\\' }; SqlString.escapeId = function escapeId(val, forbidQualified) { if (Array.isArray(val)) { var sql = ''; for (var i = 0; i < val.length; i++) { sql += (i === 0 ? '' : ', ') + SqlString.escapeId(val[i], forbidQualified); } return sql; } else if (forbidQualified) { return '`' + String(val).replace(ID_GLOBAL_REGEXP, '``') + '`'; } else { return '`' + String(val).replace(ID_GLOBAL_REGEXP, '``').replace(QUAL_GLOBAL_REGEXP, '`.`') + '`'; } }; SqlString.escape = function escape(val, stringifyObjects, timeZone) { if (val === undefined || val === null) { return 'NULL'; } switch (typeof val) { case 'boolean': return (val) ? 'true' : 'false'; case 'number': return val + ''; case 'object': if (val instanceof Date) { return SqlString.dateToString(val, timeZone || 'local'); } else if (Array.isArray(val)) { return SqlString.arrayToList(val, timeZone); } else if (Buffer.isBuffer(val)) { return SqlString.bufferToString(val); } else if (typeof val.toSqlString === 'function') { return String(val.toSqlString()); } else if (stringifyObjects) { return escapeString(val.toString()); } else { return SqlString.objectToValues(val, timeZone); } default: return escapeString(val); } }; SqlString.arrayToList = function arrayToList(array, timeZone) { var sql = ''; for (var i = 0; i < array.length; i++) { var val = array[i]; if (Array.isArray(val)) { sql += (i === 0 ? '' : ', ') + '(' + SqlString.arrayToList(val, timeZone) + ')'; } else { sql += (i === 0 ? '' : ', ') + SqlString.escape(val, true, timeZone); } } return sql; }; SqlString.format = function format(sql, values, stringifyObjects, timeZone) { if (values == null) { return sql; } if (!(values instanceof Array || Array.isArray(values))) { values = [values]; } var chunkIndex = 0; var placeholdersRegex = /\?+/g; var result = ''; var valuesIndex = 0; var match; while (valuesIndex < values.length && (match = placeholdersRegex.exec(sql))) { var len = match[0].length; if (len > 2) { continue; } var value = len === 2 ? SqlString.escapeId(values[valuesIndex]) : SqlString.escape(values[valuesIndex], stringifyObjects, timeZone); result += sql.slice(chunkIndex, match.index) + value; chunkIndex = placeholdersRegex.lastIndex; valuesIndex++; } if (chunkIndex === 0) { // Nothing was replaced return sql; } if (chunkIndex < sql.length) { return result + sql.slice(chunkIndex); } return result; }; SqlString.dateToString = function dateToString(date, timeZone) { var dt = new Date(date); if (isNaN(dt.getTime())) { return 'NULL'; } var year; var month; var day; var hour; var minute; var second; var millisecond; if (timeZone === 'local') { year = dt.getFullYear(); month = dt.getMonth() + 1; day = dt.getDate(); hour = dt.getHours(); minute = dt.getMinutes(); second = dt.getSeconds(); millisecond = dt.getMilliseconds(); } else { var tz = convertTimezone(timeZone); if (tz !== false && tz !== 0) { dt.setTime(dt.getTime() + (tz * 60000)); } year = dt.getUTCFullYear(); month = dt.getUTCMonth() + 1; day = dt.getUTCDate(); hour = dt.getUTCHours(); minute = dt.getUTCMinutes(); second = dt.getUTCSeconds(); millisecond = dt.getUTCMilliseconds(); } // YYYY-MM-DD HH:mm:ss.mmm var str = zeroPad(year, 4) + '-' + zeroPad(month, 2) + '-' + zeroPad(day, 2) + ' ' + zeroPad(hour, 2) + ':' + zeroPad(minute, 2) + ':' + zeroPad(second, 2) + '.' + zeroPad(millisecond, 3); return escapeString(str); }; SqlString.bufferToString = function bufferToString(buffer) { return 'X' + escapeString(buffer.toString('hex')); }; SqlString.objectToValues = function objectToValues(object, timeZone) { var sql = ''; for (var key in object) { var val = object[key]; if (typeof val === 'function') { continue; } sql += (sql.length === 0 ? '' : ', ') + SqlString.escapeId(key) + ' = ' + SqlString.escape(val, true, timeZone); } return sql; }; SqlString.raw = function raw(sql) { if (typeof sql !== 'string') { throw new TypeError('argument sql must be a string'); } return { toSqlString: function toSqlString() { return sql; } }; }; function escapeString(val) { var chunkIndex = CHARS_GLOBAL_REGEXP.lastIndex = 0; var escapedVal = ''; var match; while ((match = CHARS_GLOBAL_REGEXP.exec(val))) { escapedVal += val.slice(chunkIndex, match.index) + CHARS_ESCAPE_MAP[match[0]]; chunkIndex = CHARS_GLOBAL_REGEXP.lastIndex; } if (chunkIndex === 0) { // Nothing was escaped return "'" + val + "'"; } if (chunkIndex < val.length) { return "'" + escapedVal + val.slice(chunkIndex) + "'"; } return "'" + escapedVal + "'"; } function zeroPad(number, length) { number = number.toString(); while (number.length < length) { number = '0' + number; } return number; } function convertTimezone(tz) { if (tz === 'Z') { return 0; } var m = tz.match(/([\+\-\s])(\d\d):?(\d\d)?/); if (m) { return (m[1] === '-' ? -1 : 1) * (parseInt(m[2], 10) + ((m[3] ? parseInt(m[3], 10) : 0) / 60)) * 60; } return false; } sqlstring-2.3.1/package.json000066400000000000000000000022441324437525500160450ustar00rootroot00000000000000{ "name": "sqlstring", "description": "Simple SQL escape and format for MySQL", "version": "2.3.1", "contributors": [ "Adri Van Houdt ", "Douglas Christopher Wilson ", "fengmk2 (http://fengmk2.github.com)", "Kevin Jose Martin ", "Nathan Woltman ", "Sergej Sintschilin " ], "license": "MIT", "keywords": [ "sqlstring", "sql", "escape", "sql escape" ], "repository": "mysqljs/sqlstring", "devDependencies": { "beautify-benchmark": "0.2.4", "benchmark": "2.1.4", "eslint": "4.18.1", "eslint-plugin-markdown": "1.0.0-beta.6", "nyc": "10.3.2", "urun": "0.0.8", "utest": "0.0.8" }, "files": [ "lib/", "HISTORY.md", "LICENSE", "README.md", "index.js" ], "engines": { "node": ">= 0.6" }, "scripts": { "bench": "node benchmark/index.js", "lint": "eslint --plugin markdown --ext js,md .", "test": "node test/run.js", "test-ci": "nyc --reporter=text npm test", "test-cov": "nyc --reporter=html --reporter=text npm test" } } sqlstring-2.3.1/test/000077500000000000000000000000001324437525500145345ustar00rootroot00000000000000sqlstring-2.3.1/test/run.js000066400000000000000000000004551324437525500157020ustar00rootroot00000000000000var urun = require('urun'); var options = {}; if (process.env.FILTER) { options.include = new RegExp(process.env.FILTER + '.*\\.js$'); } options.reporter = 'BashTapReporter'; options.verbose = process.env.VERBOSE ? Boolean(JSON.parse(process.env.VERBOSE)) : true; urun(__dirname, options); sqlstring-2.3.1/test/unit/000077500000000000000000000000001324437525500155135ustar00rootroot00000000000000sqlstring-2.3.1/test/unit/test-SqlString.js000066400000000000000000000253401324437525500207600ustar00rootroot00000000000000var assert = require('assert'); var SqlString = require('../../'); var test = require('utest'); test('SqlString.escapeId', { 'value is quoted': function() { assert.equal(SqlString.escapeId('id'), '`id`'); }, 'value can be a number': function() { assert.equal(SqlString.escapeId(42), '`42`'); }, 'value can be an object': function() { assert.equal(SqlString.escapeId({}), '`[object Object]`'); }, 'value toString is called': function() { assert.equal(SqlString.escapeId({ toString: function() { return 'foo'; } }), '`foo`'); }, 'value toString is quoted': function() { assert.equal(SqlString.escapeId({ toString: function() { return 'f`oo'; } }), '`f``oo`'); }, 'value containing escapes is quoted': function() { assert.equal(SqlString.escapeId('i`d'), '`i``d`'); }, 'value containing separator is quoted': function() { assert.equal(SqlString.escapeId('id1.id2'), '`id1`.`id2`'); }, 'value containing separator and escapes is quoted': function() { assert.equal(SqlString.escapeId('id`1.i`d2'), '`id``1`.`i``d2`'); }, 'value containing separator is fully escaped when forbidQualified': function() { assert.equal(SqlString.escapeId('id1.id2', true), '`id1.id2`'); }, 'arrays are turned into lists': function() { assert.equal(SqlString.escapeId(['a', 'b', 't.c']), '`a`, `b`, `t`.`c`'); }, 'nested arrays are flattened': function() { assert.equal(SqlString.escapeId(['a', ['b', ['t.c']]]), '`a`, `b`, `t`.`c`'); } }); test('SqlString.escape', { 'undefined -> NULL': function() { assert.equal(SqlString.escape(undefined), 'NULL'); }, 'null -> NULL': function() { assert.equal(SqlString.escape(null), 'NULL'); }, 'booleans convert to strings': function() { assert.equal(SqlString.escape(false), 'false'); assert.equal(SqlString.escape(true), 'true'); }, 'numbers convert to strings': function() { assert.equal(SqlString.escape(5), '5'); }, 'raw not escaped': function () { assert.equal(SqlString.escape(SqlString.raw('NOW()')), 'NOW()'); }, 'objects are turned into key value pairs': function() { assert.equal(SqlString.escape({a: 'b', c: 'd'}), "`a` = 'b', `c` = 'd'"); }, 'objects function properties are ignored': function() { assert.equal(SqlString.escape({a: 'b', c: function() {}}), "`a` = 'b'"); }, 'object values toSqlString is called': function() { assert.equal(SqlString.escape({id: { toSqlString: function() { return 'LAST_INSERT_ID()'; } }}), '`id` = LAST_INSERT_ID()'); }, 'objects toSqlString is called': function() { assert.equal(SqlString.escape({ toSqlString: function() { return '@foo_id'; } }), '@foo_id'); }, 'objects toSqlString is not quoted': function() { assert.equal(SqlString.escape({ toSqlString: function() { return 'CURRENT_TIMESTAMP()'; } }), 'CURRENT_TIMESTAMP()'); }, 'nested objects are cast to strings': function() { assert.equal(SqlString.escape({a: {nested: true}}), "`a` = '[object Object]'"); }, 'nested objects use toString': function() { assert.equal(SqlString.escape({a: { toString: function() { return 'foo'; } }}), "`a` = 'foo'"); }, 'nested objects use toString is quoted': function() { assert.equal(SqlString.escape({a: { toString: function() { return "f'oo"; } }}), "`a` = 'f\\'oo'"); }, 'arrays are turned into lists': function() { assert.equal(SqlString.escape([1, 2, 'c']), "1, 2, 'c'"); }, 'nested arrays are turned into grouped lists': function() { assert.equal(SqlString.escape([[1, 2, 3], [4, 5, 6], ['a', 'b', {nested: true}]]), "(1, 2, 3), (4, 5, 6), ('a', 'b', '[object Object]')"); }, 'nested objects inside arrays are cast to strings': function() { assert.equal(SqlString.escape([1, {nested: true}, 2]), "1, '[object Object]', 2"); }, 'nested objects inside arrays use toString': function() { assert.equal(SqlString.escape([1, { toString: function() { return 'foo'; } }, 2]), "1, 'foo', 2"); }, 'strings are quoted': function() { assert.equal(SqlString.escape('Super'), "'Super'"); }, '\0 gets escaped': function() { assert.equal(SqlString.escape('Sup\0er'), "'Sup\\0er'"); assert.equal(SqlString.escape('Super\0'), "'Super\\0'"); }, '\b gets escaped': function() { assert.equal(SqlString.escape('Sup\ber'), "'Sup\\ber'"); assert.equal(SqlString.escape('Super\b'), "'Super\\b'"); }, '\n gets escaped': function() { assert.equal(SqlString.escape('Sup\ner'), "'Sup\\ner'"); assert.equal(SqlString.escape('Super\n'), "'Super\\n'"); }, '\r gets escaped': function() { assert.equal(SqlString.escape('Sup\rer'), "'Sup\\rer'"); assert.equal(SqlString.escape('Super\r'), "'Super\\r'"); }, '\t gets escaped': function() { assert.equal(SqlString.escape('Sup\ter'), "'Sup\\ter'"); assert.equal(SqlString.escape('Super\t'), "'Super\\t'"); }, '\\ gets escaped': function() { assert.equal(SqlString.escape('Sup\\er'), "'Sup\\\\er'"); assert.equal(SqlString.escape('Super\\'), "'Super\\\\'"); }, '\u001a (ascii 26) gets replaced with \\Z': function() { assert.equal(SqlString.escape('Sup\u001aer'), "'Sup\\Zer'"); assert.equal(SqlString.escape('Super\u001a'), "'Super\\Z'"); }, 'single quotes get escaped': function() { assert.equal(SqlString.escape('Sup\'er'), "'Sup\\'er'"); assert.equal(SqlString.escape('Super\''), "'Super\\''"); }, 'double quotes get escaped': function() { assert.equal(SqlString.escape('Sup"er'), "'Sup\\\"er'"); assert.equal(SqlString.escape('Super"'), "'Super\\\"'"); }, 'dates are converted to YYYY-MM-DD HH:II:SS.sss': function() { var expected = '2012-05-07 11:42:03.002'; var date = new Date(2012, 4, 7, 11, 42, 3, 2); var string = SqlString.escape(date); assert.strictEqual(string, "'" + expected + "'"); }, 'dates are converted to specified time zone "Z"': function() { var expected = '2012-05-07 11:42:03.002'; var date = new Date(Date.UTC(2012, 4, 7, 11, 42, 3, 2)); var string = SqlString.escape(date, false, 'Z'); assert.strictEqual(string, "'" + expected + "'"); }, 'dates are converted to specified time zone "+01"': function() { var expected = '2012-05-07 12:42:03.002'; var date = new Date(Date.UTC(2012, 4, 7, 11, 42, 3, 2)); var string = SqlString.escape(date, false, '+01'); assert.strictEqual(string, "'" + expected + "'"); }, 'dates are converted to specified time zone "+0200"': function() { var expected = '2012-05-07 13:42:03.002'; var date = new Date(Date.UTC(2012, 4, 7, 11, 42, 3, 2)); var string = SqlString.escape(date, false, '+0200'); assert.strictEqual(string, "'" + expected + "'"); }, 'dates are converted to specified time zone "-05:00"': function() { var expected = '2012-05-07 06:42:03.002'; var date = new Date(Date.UTC(2012, 4, 7, 11, 42, 3, 2)); var string = SqlString.escape(date, false, '-05:00'); assert.strictEqual(string, "'" + expected + "'"); }, 'dates are converted to UTC for unknown time zone': function() { var date = new Date(Date.UTC(2012, 4, 7, 11, 42, 3, 2)); var expected = SqlString.escape(date, false, 'Z'); var string = SqlString.escape(date, false, 'foo'); assert.strictEqual(string, expected); }, 'invalid dates are converted to null': function() { var date = new Date(NaN); var string = SqlString.escape(date); assert.strictEqual(string, 'NULL'); }, 'buffers are converted to hex': function() { var buffer = new Buffer([0, 1, 254, 255]); var string = SqlString.escape(buffer); assert.strictEqual(string, "X'0001feff'"); }, 'buffers object cannot inject SQL': function() { var buffer = new Buffer([0, 1, 254, 255]); buffer.toString = function() { return "00' OR '1'='1"; }; var string = SqlString.escape(buffer); assert.strictEqual(string, "X'00\\' OR \\'1\\'=\\'1'"); }, 'NaN -> NaN': function() { assert.equal(SqlString.escape(NaN), 'NaN'); }, 'Infinity -> Infinity': function() { assert.equal(SqlString.escape(Infinity), 'Infinity'); } }); test('SqlString.format', { 'question marks are replaced with escaped array values': function() { var sql = SqlString.format('? and ?', ['a', 'b']); assert.equal(sql, "'a' and 'b'"); }, 'double quest marks are replaced with escaped id': function () { var sql = SqlString.format('SELECT * FROM ?? WHERE id = ?', ['table', 42]); assert.equal(sql, 'SELECT * FROM `table` WHERE id = 42'); }, 'triple question marks are ignored': function () { var sql = SqlString.format('? or ??? and ?', ['foo', 'bar', 'fizz', 'buzz']); assert.equal(sql, "'foo' or ??? and 'bar'"); }, 'extra question marks are left untouched': function() { var sql = SqlString.format('? and ?', ['a']); assert.equal(sql, "'a' and ?"); }, 'extra arguments are not used': function() { var sql = SqlString.format('? and ?', ['a', 'b', 'c']); assert.equal(sql, "'a' and 'b'"); }, 'question marks within values do not cause issues': function() { var sql = SqlString.format('? and ?', ['hello?', 'b']); assert.equal(sql, "'hello?' and 'b'"); }, 'undefined is ignored': function () { var sql = SqlString.format('?', undefined, false); assert.equal(sql, '?'); }, 'objects is converted to values': function () { var sql = SqlString.format('?', { 'hello': 'world' }, false); assert.equal(sql, "`hello` = 'world'"); }, 'objects is not converted to values': function () { var sql = SqlString.format('?', { 'hello': 'world' }, true); assert.equal(sql, "'[object Object]'"); var sql = SqlString.format('?', { toString: function () { return 'hello'; } }, true); assert.equal(sql, "'hello'"); var sql = SqlString.format('?', { toSqlString: function () { return '@foo'; } }, true); assert.equal(sql, '@foo'); }, 'sql is untouched if no values are provided': function () { var sql = SqlString.format('SELECT ??'); assert.equal(sql, 'SELECT ??'); }, 'sql is untouched if values are provided but there are no placeholders': function () { var sql = SqlString.format('SELECT COUNT(*) FROM table', ['a', 'b']); assert.equal(sql, 'SELECT COUNT(*) FROM table'); } }); test('SqlString.raw', { 'creates object': function() { assert.equal(typeof SqlString.raw('NOW()'), 'object'); }, 'rejects number': function() { assert.throws(function () { SqlString.raw(42); }); }, 'rejects undefined': function() { assert.throws(function () { SqlString.raw(); }); }, 'object has toSqlString': function() { assert.equal(typeof SqlString.raw('NOW()').toSqlString, 'function'); }, 'toSqlString returns sql as-is': function() { assert.equal(SqlString.raw("NOW() AS 'current_time'").toSqlString(), "NOW() AS 'current_time'"); } });