├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── example.js ├── package.json ├── sql-escape-string.js └── test └── sql-escape-string.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 6 4 | - 8 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Thorsten Lorenz. 2 | All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person 5 | obtaining a copy of this software and associated documentation 6 | files (the "Software"), to deal in the Software without 7 | restriction, including without limitation the rights to use, 8 | copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sql-escape-string [![build status](https://secure.travis-ci.org/thlorenz/sql-escape-string.svg?branch=master)](http://travis-ci.org/thlorenz/sql-escape-string) 2 | 3 | Simple SQL string escape. 4 | 5 | ```js 6 | const escapeString = require('sql-string-escape') 7 | const sqlString = "Sup'er" 8 | console.log(escapeString(sqlString)) // => Sup''er 9 | ``` 10 | 11 | ## Installation 12 | 13 | npm install sql-escape-string 14 | 15 | ## Note 16 | 17 | Original implementation from [sqlstring](https://github.com/mysqljs/sqlstring) with the added 18 | option of supporting or not supporting backslash. 19 | 20 | ## [API](https://thlorenz.github.io/sql-escape-string) 21 | 22 | 23 | 24 | ### escapeString 25 | 26 | Escapes the given string to protect against SQL injection attacks. 27 | 28 | By default it assumes that backslashes are not supported as they are not part of the standard SQL spec. 29 | Quoting from the [SQLlite web site](https://sqlite.org/lang_expr.html): 30 | 31 | > C-style escapes using the backslash character are not supported because they are not standard SQL. 32 | 33 | This means three things: 34 | 35 | - backslashes and double quotes `"` are not escaped by default 36 | - single quotes are escaped via `''` instead of `\'` 37 | - your sql engine should throw an error when encountering a backslash escape 38 | as part of a string, unless it is a literal backslash, i.e. `'backslash: \\'`. 39 | 40 | It is recommended to set the `backslashSupported` option `true` if your SQL 41 | engine supports it. In that case backslash sequences are escaped and single 42 | and double quotes are escaped via a backslash, i.e. `'\''`. 43 | 44 | **Parameters** 45 | 46 | - `val` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the original string to be used in a SQL query 47 | - `$0` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** opts 48 | - `$0.backslashSupported` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** if `true` backslashes are supported (optional, default `false`) 49 | - `opts` 50 | 51 | Returns **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the original string escaped wrapped in single quotes, i.e. `'mystring'` 52 | 53 | ## License 54 | 55 | MIT 56 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | const escapeString = require('./') 2 | const sqlString = "Sup'er" 3 | console.log(escapeString(sqlString)) // => Sup''er 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sql-escape-string", 3 | "version": "1.1.0", 4 | "description": "Simple SQL string escape.", 5 | "main": "sql-escape-string.js", 6 | "scripts": { 7 | "test": "tape test/*.js" 8 | }, 9 | "files": [ 10 | "sql-escape-string.js" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/thlorenz/sql-escape-string.git" 15 | }, 16 | "homepage": "https://github.com/thlorenz/sql-escape-string", 17 | "dependencies": {}, 18 | "devDependencies": { 19 | "tape": "^4.8.0" 20 | }, 21 | "keywords": [ 22 | "sql", 23 | "escape", 24 | "injection", 25 | "security" 26 | ], 27 | "author": { 28 | "name": "Thorsten Lorenz", 29 | "email": "thlorenz@gmx.de", 30 | "url": "http://thlorenz.com" 31 | }, 32 | "license": { 33 | "type": "MIT", 34 | "url": "https://github.com/thlorenz/sql-escape-string/blob/master/LICENSE" 35 | }, 36 | "engine": { 37 | "node": ">=6" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sql-escape-string.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // eslint-disable-next-line no-control-regex 4 | const CHARS_GLOBAL_BACKSLASH_SUPPORTED_RX = /[\0\b\t\n\r\x1a"'\\]/g 5 | const CHARS_ESCAPE_BACKSLASH_SUPPORTED_MAP = { 6 | '\0' : '\\0' 7 | , '\b' : '\\b' 8 | , '\t' : '\\t' 9 | , '\n' : '\\n' 10 | , '\r' : '\\r' 11 | , '\x1a' : '\\Z' 12 | , '"' : '\\"' 13 | , '\'' : '\\\'' 14 | , '\\' : '\\\\' 15 | } 16 | 17 | /** 18 | * Escapes the given string to protect against SQL injection attacks. 19 | * 20 | * By default it assumes that backslashes are not supported as they are not part of the standard SQL spec. 21 | * Quoting from the [SQLlite web site](https://sqlite.org/lang_expr.html): 22 | * 23 | * > C-style escapes using the backslash character are not supported because they are not standard SQL. 24 | * 25 | * This means three things: 26 | * 27 | * - backslashes and double quotes `"` are not escaped by default 28 | * - single quotes are escaped via `''` instead of `\'` 29 | * - your sql engine should throw an error when encountering a backslash escape 30 | * as part of a string, unless it is a literal backslash, i.e. `'backslash: \\'`. 31 | * 32 | * It is recommended to set the `backslashSupported` option `true` if your SQL 33 | * engine supports it. In that case backslash sequences are escaped and single 34 | * and double quotes are escaped via a backslash, i.e. `'\''`. 35 | * 36 | * @param {String} val the original string to be used in a SQL query 37 | * @param {Object} $0 opts 38 | * @param {Boolean} [$0.backslashSupported = false] if `true` backslashes are supported 39 | * @returns {String} the original string escaped wrapped in single quotes, i.e. `'mystring'` 40 | */ 41 | function escapeString(val, opts) { 42 | if (val == null) { 43 | throw new Error('Need to pass a valid string') 44 | } 45 | opts = opts || {} 46 | const backslashSupported = !!opts.backslashSupported 47 | 48 | if (!backslashSupported) return "'" + val.replace(/'/g, "''") + "'" 49 | 50 | const charsRx = CHARS_GLOBAL_BACKSLASH_SUPPORTED_RX 51 | const charsEscapeMap = CHARS_ESCAPE_BACKSLASH_SUPPORTED_MAP 52 | var chunkIndex = charsRx.lastIndex = 0 53 | var escapedVal = '' 54 | var match 55 | 56 | while ((match = charsRx.exec(val))) { 57 | escapedVal += val.slice(chunkIndex, match.index) + charsEscapeMap[match[0]] 58 | chunkIndex = charsRx.lastIndex 59 | } 60 | 61 | // Nothing was escaped 62 | if (chunkIndex === 0) return "'" + val + "'" 63 | 64 | if (chunkIndex < val.length) return "'" + escapedVal + val.slice(chunkIndex) + "'" 65 | return "'" + escapedVal + "'" 66 | } 67 | 68 | module.exports = escapeString 69 | -------------------------------------------------------------------------------- /test/sql-escape-string.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const test = require('tape') 4 | const escapeString = require('../') 5 | 6 | test('\nescapeString: valid values backslash supported', function(t) { 7 | [ [ 'NOW()', "'NOW()'" ] 8 | , [ 'Super', "'Super'" ] 9 | , [ 'Sup\0er', "'Sup\\0er'" ] 10 | , [ 'Super\0', "'Super\\0'" ] 11 | , [ 'Sup\ber', "'Sup\\ber'" ] 12 | , [ 'Super\b', "'Super\\b'" ] 13 | , [ 'Sup\ner', "'Sup\\ner'" ] 14 | , [ 'Super\n', "'Super\\n'" ] 15 | , [ 'Sup\rer', "'Sup\\rer'" ] 16 | , [ 'Super\r', "'Super\\r'" ] 17 | , [ 'Sup\ter', "'Sup\\ter'" ] 18 | , [ 'Super\t', "'Super\\t'" ] 19 | , [ 'Sup\\er', "'Sup\\\\er'" ] 20 | , [ 'Super\\', "'Super\\\\'" ] 21 | , [ 'Sup\u001aer', "'Sup\\Zer'" ] 22 | , [ 'Super\u001a', "'Super\\Z'" ] 23 | , [ 'Sup\'er', "'Sup\\'er'" ] 24 | , [ 'Super\'', "'Super\\''" ] 25 | // eslint-disable-next-line no-useless-escape 26 | , [ 'Sup"er', "'Sup\\\"er'" ] 27 | // eslint-disable-next-line no-useless-escape 28 | , [ 'Super"', "'Super\\\"'" ] 29 | 30 | ].forEach(function check(xs) { 31 | const s = xs[0] 32 | const escaped = xs[1] 33 | const msg = 'escapes: "' + s + '" to "' + escaped + '"' 34 | const res = escapeString(s, { backslashSupported: true }) 35 | t.equal(res, escaped, msg) 36 | }) 37 | 38 | t.end() 39 | }) 40 | 41 | test('\nescapeString: valid values backslash not supported', function(t) { 42 | [ [ 'NOW()', "'NOW()'" ] 43 | , [ 'Super', "'Super'" ] 44 | , [ 'Sup\0er', "'Sup\0er'" ] 45 | , [ 'Super\0', "'Super\0'" ] 46 | , [ 'Sup\ber', "'Sup\ber'" ] 47 | , [ 'Super\b', "'Super\b'" ] 48 | , [ 'Sup\ner', "'Sup\ner'" ] 49 | , [ 'Super\n', "'Super\n'" ] 50 | , [ 'Sup\rer', "'Sup\rer'" ] 51 | , [ 'Super\r', "'Super\r'" ] 52 | , [ 'Sup\ter', "'Sup\ter'" ] 53 | , [ 'Super\t', "'Super\t'" ] 54 | , [ 'Sup\\er', "'Sup\\er'" ] 55 | , [ 'Super\\', "'Super\\'" ] 56 | , [ 'Sup\u001aer', "'Sup\u001aer'" ] 57 | , [ 'Super\u001a', "'Super\u001a'" ] 58 | , [ 'Sup\'er', "'Sup''er'" ] 59 | , [ 'Sup\'e\'r', "'Sup''e''r'" ] 60 | , [ 'Super\'', "'Super'''" ] 61 | // eslint-disable-next-line no-useless-escape 62 | , [ 'Sup"er', "'Sup\"er'" ] 63 | // eslint-disable-next-line no-useless-escape 64 | , [ 'Super"', "'Super\"'" ] 65 | 66 | ].forEach(function check(xs) { 67 | const s = xs[0] 68 | const escaped = xs[1] 69 | const msg = 'escapes: "' + s + '" to "' + escaped + '"' 70 | const res = escapeString(s, { backslashSupported: false }) 71 | t.equal(res, escaped, msg) 72 | }) 73 | 74 | t.end() 75 | }) 76 | 77 | test('\nescapeString: invalid values', function(t) { 78 | [ undefined 79 | , null 80 | ].forEach(function check(s) { 81 | const msg = 'escaping "' + s + ' throws' 82 | t.throws(escapeString.bind(null, s), msg) 83 | }) 84 | 85 | t.end() 86 | }) 87 | --------------------------------------------------------------------------------