├── .gitignore ├── .npmignore ├── CHANGES.md ├── LICENSE ├── README.md ├── bin ├── ssha-list └── ssha-sign ├── lib └── ssh_agent_client.js ├── package.json ├── test └── agent.test.js └── tools ├── jsl.node.conf └── jsstyle.conf /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | tools 3 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # ssh-agent Changelog 2 | 3 | ## 0.2.4 4 | - Fix bug where request callback can be called more than once 5 | 6 | ## 0.2.3 7 | 8 | - Update ctype and posix-getopt dependencies 9 | - Switch unit tests to use node-tape 10 | - Fix error when timeout not initialized 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Mark Cavage, All rights reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-ssh-agent is a client binding to the SSH Agent protocol, written in "pure" 2 | node.js. For now, the operations supported are "list keys" and "sign data" 3 | (which in SSH parlance is `requestIdentities` and `sign`. 4 | 5 | ## Usage 6 | 7 | var SSHAgentClient = require('ssh-agent'); 8 | 9 | var client = new SSHAgentClient(); 10 | var data = new Buffer('Hello World'); 11 | 12 | // Try to sign data with an RSA key (will generate 13 | // an RSA-SHA1 signature). 14 | client.requestIdentities(function(err, keys) { 15 | var key = null; 16 | for (var i = 0; i < keys.length; i++) { 17 | if (keys[i].type === 'ssh-rsa') { 18 | key = keys[i]; 19 | break; 20 | } 21 | } 22 | if (!key) 23 | return; 24 | 25 | client.sign(key, data, function(err, signature) { 26 | console.log('Signature: ' + signature.signature); 27 | }); 28 | }); 29 | 30 | 31 | ## Installation 32 | 33 | npm install ssh-agent 34 | 35 | ## License 36 | 37 | MIT. 38 | 39 | ## Bugs 40 | 41 | See . 42 | -------------------------------------------------------------------------------- /bin/ssha-list: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // -*- mode: js -*- 3 | 4 | var getopt = require('posix-getopt'); 5 | var path = require('path'); 6 | var SSHAgentClient = require(path.resolve(__dirname, '../lib/ssh_agent_client.js')); 7 | 8 | 9 | 10 | ///--- Functions 11 | 12 | function usage(msg, code) { 13 | if (msg) 14 | console.error(msg); 15 | 16 | var str = 'usage: ' + path.basename(process.argv[1]); 17 | console.error(str); 18 | process.exit(code || 0); 19 | } 20 | 21 | 22 | function parseOptions() { 23 | var option; 24 | var parser = new getopt.BasicParser('h', process.argv); 25 | var tmp; 26 | 27 | while ((option = parser.getopt()) !== undefined && !option.error) { 28 | switch (option.option) { 29 | case 'h': 30 | usage(); 31 | break; 32 | 33 | default: 34 | usage('invalid option', 1); 35 | break; 36 | } 37 | } 38 | } 39 | 40 | 41 | 42 | ///--- Mainline 43 | 44 | parseOptions(); 45 | 46 | (new SSHAgentClient()).requestIdentities(function(err, keys) { 47 | if (err) { 48 | console.error(err.toString()); 49 | process.exit(1); 50 | } 51 | 52 | console.log(JSON.stringify(keys.map(function (k) { 53 | return ({ 54 | key: k.ssh_key, 55 | type: k.type, 56 | name: k.comment 57 | }); 58 | }), null, 2)); 59 | 60 | process.exit(0); 61 | }); -------------------------------------------------------------------------------- /bin/ssha-sign: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // -*- mode: js -*- 3 | 4 | var getopt = require('posix-getopt'); 5 | var path = require('path'); 6 | var SSHAgentClient = require(path.resolve(__dirname, '../lib/ssh_agent_client.js')); 7 | 8 | 9 | 10 | ///--- Functions 11 | 12 | function usage(msg, code) { 13 | if (msg) 14 | console.error(msg); 15 | 16 | var str = 'usage: ' + path.basename(process.argv[1]); 17 | str += ' key_name ...'; 18 | console.error(str); 19 | process.exit(code || 0); 20 | } 21 | 22 | 23 | function parseOptions() { 24 | var option; 25 | var parser = new getopt.BasicParser('h', process.argv); 26 | var tmp; 27 | 28 | while ((option = parser.getopt()) !== undefined && !option.error) { 29 | switch (option.option) { 30 | case 'h': 31 | usage(); 32 | break; 33 | 34 | default: 35 | usage('invalid option', 1); 36 | break; 37 | } 38 | } 39 | 40 | if (parser.optind() >= process.argv.length) 41 | usage('missing required argument: "name"'); 42 | 43 | return (process.argv.slice(parser.optind()).pop()); 44 | } 45 | 46 | 47 | 48 | ///--- Mainline 49 | 50 | var client = new SSHAgentClient(); 51 | var kname = parseOptions(); 52 | 53 | client.requestIdentities(function(err, keys) { 54 | if (err) { 55 | console.error(err.toString()); 56 | process.exit(1); 57 | } 58 | 59 | var key = keys.filter(function (k) { 60 | return (k.comment === kname); 61 | }).pop(); 62 | 63 | if (!key) 64 | usage(kname + ' invalid', 1); 65 | 66 | var data = ''; 67 | process.stdin.resume(); 68 | process.stdin.setEncoding('utf8'); 69 | 70 | process.stdin.on('data', function (chunk) { 71 | data += chunk; 72 | }); 73 | 74 | process.stdin.on('end', function () { 75 | client.sign(key, new Buffer(data), function(err2, signature) { 76 | if (err2) { 77 | console.error(err2.toString()); 78 | process.exit(1); 79 | } 80 | console.log(signature.signature); 81 | }); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /lib/ssh_agent_client.js: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Mark Cavage All rights reserved. 2 | var assert = require('assert'); 3 | var net = require('net'); 4 | var util = require('util'); 5 | 6 | var ctype = require('ctype'); 7 | 8 | 9 | 10 | ///--- Globals 11 | 12 | var PROTOCOL = { 13 | SSH_AGENTC_REQUEST_RSA_IDENTITIES: 11, 14 | SSH_AGENT_IDENTITIES_ANSWER: 12, 15 | SSH2_AGENTC_SIGN_REQUEST: 13, 16 | SSH2_AGENT_SIGN_RESPONSE: 14, 17 | SSH_AGENT_FAILURE: 5, 18 | SSH_AGENT_SUCCESS: 6 19 | }; 20 | 21 | 22 | 23 | ///--- Specific Errors 24 | 25 | function MissingEnvironmentVariableError(variable) { 26 | this.name = 'MissingEnvironmentVariableError'; 27 | this.message = variable + ' was not found in your environment'; 28 | this.variable = variable; 29 | Error.captureStackTrace(this, MissingEnvironmentVariableError); 30 | } 31 | util.inherits(MissingEnvironmentVariableError, Error); 32 | 33 | 34 | function TimeoutError(message) { 35 | this.name = 'TimeoutError'; 36 | this.message = message; 37 | Error.captureStackTrace(this, TimeoutError); 38 | } 39 | util.inherits(TimeoutError, Error); 40 | 41 | 42 | function InvalidProtocolError(message) { 43 | this.name = 'InvalidProtocolError'; 44 | this.message = message; 45 | Error.captureStackTrace(this, InvalidProtocolError); 46 | } 47 | util.inherits(InvalidProtocolError, Error); 48 | 49 | 50 | 51 | ///--- Internal Helpers 52 | 53 | function _newBuffer(buffers, additional) { 54 | assert.ok(buffers); 55 | 56 | var len = 5; // length + tag 57 | for (var i = 0; i < buffers.length; i++) 58 | len += 4 + buffers[i].length; 59 | 60 | if (additional) 61 | len += additional; 62 | 63 | return new Buffer(len); 64 | } 65 | 66 | 67 | function _readString(buffer, offset) { 68 | assert.ok(buffer); 69 | assert.ok(offset !== undefined); 70 | 71 | var len = ctype.ruint32(buffer, 'big', offset); 72 | offset += 4; 73 | 74 | var str = new Buffer(len); 75 | buffer.copy(str, 0, offset, offset + len); 76 | 77 | return str; 78 | } 79 | 80 | 81 | function _writeString(request, buffer, offset) { 82 | assert.ok(request); 83 | assert.ok(buffer); 84 | assert.ok(offset !== undefined); 85 | 86 | ctype.wuint32(buffer.length, 'big', request, offset); 87 | offset += 4; 88 | buffer.copy(request, offset); 89 | 90 | return offset + buffer.length; 91 | } 92 | 93 | 94 | function _readHeader(response, expect) { 95 | assert.ok(response); 96 | 97 | var len = ctype.ruint32(response, 'big', 0); 98 | var type = ctype.ruint8(response, 'big', 4); 99 | 100 | return (expect === type ? len : -1); 101 | } 102 | 103 | 104 | function _writeHeader(request, tag) { 105 | ctype.wuint32(request.length - 4, 'big', request, 0); 106 | ctype.wuint8(tag, 'big', request, 4); 107 | return 5; 108 | } 109 | 110 | 111 | 112 | ///--- API 113 | 114 | /** 115 | * Creates a new SSHAgentClient. 116 | * 117 | * Note that the environment variable SSH_AUTH_SOCK must be set, else 118 | * this will throw. 119 | * 120 | * @param {Object} options (optional) only supported flag is timeout (in ms). 121 | * @throws {MissingEnvironmentVariableError} on SSH_AUTH_SOCK not being set. 122 | * @constructor 123 | */ 124 | function SSHAgentClient(options) { 125 | options = options || {}; 126 | this.timeout = options.timeout || 1000; 127 | 128 | this.sockFile = process.env.SSH_AUTH_SOCK; 129 | if (!this.sockFile) 130 | throw new MissingEnvironmentVariableError('SSH_AUTH_SOCK'); 131 | } 132 | 133 | 134 | /** 135 | * Lists all SSH keys available under this session. 136 | * 137 | * This returns an array of objects of the form: 138 | * { 139 | * type: 'ssh-rsa', 140 | * ssh_key: '', 141 | * comment: '/Users/mark/.ssh/id_rsa' 142 | * } 143 | * 144 | * @param {Function} callback of the form f(err, keys). 145 | * @throws {TypeError} on invalid arguments. 146 | */ 147 | SSHAgentClient.prototype.requestIdentities = function (callback) { 148 | if (!callback || typeof(callback) !== 'function') 149 | throw new TypeError('callback (function) is required'); 150 | 151 | function requestIdentities() { 152 | var request = new Buffer(4 + 1); 153 | _writeHeader(request, PROTOCOL.SSH_AGENTC_REQUEST_RSA_IDENTITIES); 154 | return request; 155 | } 156 | 157 | function identitiesAnswer(response) { 158 | assert.ok(response); 159 | 160 | var numKeys = ctype.ruint32(response, 'big', 0); 161 | 162 | var offset = 4; 163 | var keys = []; 164 | for (var i = 0; i < numKeys; i++) { 165 | var key = _readString(response, offset); 166 | offset += 4 + key.length; 167 | var comment = _readString(response, offset); 168 | offset += 4 + comment.length; 169 | var type = _readString(key, 0); 170 | 171 | keys.push({ 172 | type: type.toString('ascii'), 173 | ssh_key: key.toString('base64'), 174 | comment: comment.toString('utf8'), 175 | _raw: key 176 | }); 177 | } 178 | 179 | return callback(null, keys); 180 | } 181 | 182 | return this._request(requestIdentities, 183 | identitiesAnswer, 184 | PROTOCOL.SSH_AGENT_IDENTITIES_ANSWER, 185 | callback); 186 | }; 187 | 188 | 189 | /** 190 | * Asks the SSH Agent to sign some data given a key. 191 | * 192 | * The key object MUST be the object retrieved from 193 | * requestIdentities. Data is a Buffer. The response 194 | * you get back is an object of the form: 195 | * 196 | * { 197 | * type: 'ssh-rsa', 198 | * signature: 'base64 string' 199 | * } 200 | * 201 | * @param {Object} key a key from requestIdentities. 202 | * @param {Object} data a Buffer. 203 | * @param {Function} callback of the form f(err, signature). 204 | * @throws {TypeError} on invalid arguments. 205 | */ 206 | SSHAgentClient.prototype.sign = function (key, data, callback) { 207 | if (!key || typeof(key) !== 'object') 208 | throw new TypeError('key (object) required'); 209 | if (!data || typeof(data) !== 'object') 210 | throw new TypeError('key (buffer) required'); 211 | if (!callback || typeof(callback) !== 'function') 212 | throw new TypeError('callback (function) is required'); 213 | 214 | function signRequest() { 215 | // Length + tag + 2 length prefixed strings + trailing flags(NULL) 216 | var request = new Buffer(4 + 1 + 4 + key._raw.length + 4 + data.length + 4); 217 | var offset = _writeHeader(request, PROTOCOL.SSH2_AGENTC_SIGN_REQUEST); 218 | offset = _writeString(request, key._raw, offset); 219 | offset = _writeString(request, data, offset); 220 | ctype.wuint32(0, 'big', request, offset); 221 | return request; 222 | } 223 | 224 | function signatureResponse(response) { 225 | assert.ok(response); 226 | 227 | var blob = _readString(response, 0); 228 | var type = _readString(blob, 0); 229 | var signature = _readString(blob, type.length + 4); 230 | 231 | return callback(null, { 232 | type: type, 233 | signature: signature.toString('base64'), 234 | _raw: signature 235 | }); 236 | } 237 | 238 | return this._request(signRequest, 239 | signatureResponse, 240 | PROTOCOL.SSH2_AGENT_SIGN_RESPONSE, 241 | callback); 242 | }; 243 | 244 | 245 | 246 | ///--- Private Methods 247 | 248 | SSHAgentClient.prototype._request = function (getRequest, 249 | parseResponse, 250 | messageType, 251 | callback) { 252 | assert.ok(getRequest && typeof(getRequest) === 'function'); 253 | assert.ok(parseResponse && typeof(parseResponse) === 'function'); 254 | assert.ok(messageType && typeof(messageType) === 'number'); 255 | assert.ok(callback && typeof(callback) === 'function'); 256 | 257 | var self = this; 258 | var socket = net.createConnection(this.sockFile); 259 | var gotdata = false; 260 | var timedout = false; 261 | 262 | socket.on('data', function (data) { 263 | gotdata = true; 264 | socket.end(); 265 | 266 | var len = ctype.ruint32(data, 'big', 0); 267 | if (len !== data.length - 4) { 268 | return callback(new InvalidProtocolError('Expected length: ' + 269 | len + ' but got: ' + 270 | data.length)); 271 | } 272 | 273 | var type = ctype.ruint8(data, 'big', 4); 274 | if (type !== messageType) { 275 | return callback(new InvalidProtocolError('Expected message type: ' + 276 | messageType + 277 | ' but got: ' + type)); 278 | } 279 | 280 | return parseResponse(data.slice(5)); 281 | }); 282 | 283 | socket.on('close', function (haderror) { 284 | if (!haderror && !gotdata && !timedout) { 285 | callback(new InvalidProtocolError('No Response')); 286 | } 287 | }); 288 | 289 | socket.on('connect', function () { 290 | socket.write(getRequest()); 291 | }); 292 | 293 | socket.on('error', function (err) { 294 | return callback(err); 295 | }); 296 | 297 | socket.setTimeout(this.timeout, function () { 298 | timedout = true; 299 | socket.destroy(); 300 | var e = new TimeoutError('request timed out after: ' + self.timeout); 301 | return callback(e); 302 | }); 303 | 304 | return socket; 305 | }; 306 | 307 | 308 | 309 | module.exports = SSHAgentClient; 310 | 311 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Mark Cavage ", 3 | "contributors": [ 4 | "Dave Eddy ", 5 | "Patrick Mooney ", 6 | "Dave Pacheco " 7 | ], 8 | "name": "ssh-agent", 9 | "description": "An API for interacting with the SSH Agent.", 10 | "version": "0.2.4", 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/mcavage/node-ssh-agent.git" 14 | }, 15 | "bin": { 16 | "ssha-list": "./bin/ssha-list", 17 | "ssha-sign": "./bin/ssha-sign" 18 | }, 19 | "main": "lib/ssh_agent_client", 20 | "engines": { 21 | "node": ">= 0.8" 22 | }, 23 | "dependencies": { 24 | "ctype": "0.5.4", 25 | "posix-getopt": "1.1.0" 26 | }, 27 | "devDependencies": { 28 | "faucet": "0.0.1", 29 | "tape": "3.5.0" 30 | }, 31 | "scripts": { 32 | "test": "node ./test/agent.test.js | ./node_modules/.bin/faucet" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/agent.test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Mark Cavage All rights reserved. 2 | 3 | var crypto = require('crypto'); 4 | var fs = require('fs'); 5 | var util = require('util'); 6 | var test = require('tape').test; 7 | 8 | 9 | ///--- Globals 10 | 11 | var SSHAgentClient; 12 | var client = null; 13 | var privateKey = null; 14 | 15 | 16 | ///--- Start Tests 17 | 18 | test('require library', function (t) { 19 | SSHAgentClient = require('../lib/ssh_agent_client'); 20 | t.ok(SSHAgentClient); 21 | t.end(); 22 | }); 23 | 24 | test('setup', function (t) { 25 | client = new SSHAgentClient(); 26 | t.ok(client); 27 | 28 | if (process.env.SSH_PRIVATE_KEY) 29 | privateKey = fs.readFileSync(process.env.SSH_PRIVATE_KEY, 'ascii'); 30 | 31 | t.end(); 32 | }); 33 | 34 | test('request identities', function (t) { 35 | client.requestIdentities(function (err, keys) { 36 | t.ifError(err); 37 | t.ok(keys); 38 | t.ok(keys.length); 39 | t.ok(keys[0].type); 40 | t.ok(keys[0].ssh_key); 41 | t.ok(keys[0].comment); 42 | t.ok(keys[0]._raw); 43 | t.end(); 44 | }); 45 | }); 46 | 47 | test('sign', function (t) { 48 | client.requestIdentities(function (err, keys) { 49 | t.ifError(err); 50 | t.ok(keys); 51 | t.ok(keys.length); 52 | 53 | var key = keys[0]; 54 | for (var i = 0; i < keys.length; i++) { 55 | if (keys[i].type === 'ssh-rsa') { 56 | key = keys[i]; 57 | break; 58 | } 59 | } 60 | 61 | var data = new Buffer('Hello World'); 62 | client.sign(key, data, function (err, signature) { 63 | t.ifError(err); 64 | t.ok(signature); 65 | 66 | if (privateKey) { 67 | var signer = crypto.createSign('RSA-SHA1'); 68 | signer.update(data); 69 | t.equal(signature.signature, signer.sign(privateKey, 'base64')); 70 | } 71 | 72 | t.end(); 73 | }); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /tools/jsl.node.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Configuration File for JavaScript Lint 3 | # 4 | # This configuration file can be used to lint a collection of scripts, or to enable 5 | # or disable warnings for scripts that are linted via the command line. 6 | # 7 | 8 | ### Warnings 9 | # Enable or disable warnings based on requirements. 10 | # Use "+WarningName" to display or "-WarningName" to suppress. 11 | # 12 | +ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent 13 | +ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity 14 | +ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement 15 | +anon_no_return_value # anonymous function does not always return value 16 | +assign_to_function_call # assignment to a function call 17 | -block_without_braces # block statement without curly braces 18 | +comma_separated_stmts # multiple statements separated by commas (use semicolons?) 19 | +comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) 20 | +default_not_at_end # the default case is not at the end of the switch statement 21 | +dup_option_explicit # duplicate "option explicit" control comment 22 | +duplicate_case_in_switch # duplicate case in switch statement 23 | +duplicate_formal # duplicate formal argument {name} 24 | +empty_statement # empty statement or extra semicolon 25 | +identifier_hides_another # identifer {name} hides an identifier in a parent scope 26 | -inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement 27 | +incorrect_version # Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version. 28 | +invalid_fallthru # unexpected "fallthru" control comment 29 | +invalid_pass # unexpected "pass" control comment 30 | +jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax 31 | +leading_decimal_point # leading decimal point may indicate a number or an object member 32 | +legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax 33 | +meaningless_block # meaningless block; curly braces have no impact 34 | +mismatch_ctrl_comments # mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence 35 | +misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma 36 | +missing_break # missing break statement 37 | +missing_break_for_last_case # missing break statement for last case in switch 38 | +missing_default_case # missing default case in switch statement 39 | +missing_option_explicit # the "option explicit" control comment is missing 40 | +missing_semicolon # missing semicolon 41 | +missing_semicolon_for_lambda # missing semicolon for lambda assignment 42 | +multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs 43 | +nested_comment # nested comment 44 | +no_return_value # function {name} does not always return a value 45 | +octal_number # leading zeros make an octal number 46 | +parseint_missing_radix # parseInt missing radix parameter 47 | +partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag 48 | +redeclared_var # redeclaration of {name} 49 | +trailing_comma_in_array # extra comma is not recommended in array initializers 50 | +trailing_decimal_point # trailing decimal point may indicate a number or an object member 51 | +undeclared_identifier # undeclared identifier: {name} 52 | +unreachable_code # unreachable code 53 | -unreferenced_argument # argument declared but never referenced: {name} 54 | -unreferenced_function # function is declared but never referenced: {name} 55 | +unreferenced_variable # variable is declared but never referenced: {name} 56 | +unsupported_version # JavaScript {version} is not supported 57 | +use_of_label # use of label 58 | +useless_assign # useless assignment 59 | +useless_comparison # useless comparison; comparing identical expressions 60 | -useless_quotes # the quotation marks are unnecessary 61 | +useless_void # use of the void type may be unnecessary (void is always undefined) 62 | +var_hides_arg # variable {name} hides argument 63 | +want_assign_or_call # expected an assignment or function call 64 | +with_statement # with statement hides undeclared variables; use temporary variable instead 65 | -identifier_hides_another 66 | 67 | ### Output format 68 | # Customize the format of the error message. 69 | # __FILE__ indicates current file path 70 | # __FILENAME__ indicates current file name 71 | # __LINE__ indicates current line 72 | # __COL__ indicates current column 73 | # __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__) 74 | # __ERROR_NAME__ indicates error name (used in configuration file) 75 | # __ERROR_PREFIX__ indicates error prefix 76 | # __ERROR_MSG__ indicates error message 77 | # 78 | # For machine-friendly output, the output format can be prefixed with 79 | # "encode:". If specified, all items will be encoded with C-slashes. 80 | # 81 | # Visual Studio syntax (default): 82 | +output-format __FILE__(__LINE__): __ERROR__ 83 | # Alternative syntax: 84 | #+output-format __FILE__:__LINE__: __ERROR__ 85 | 86 | 87 | ### Context 88 | # Show the in-line position of the error. 89 | # Use "+context" to display or "-context" to suppress. 90 | # 91 | +context 92 | 93 | 94 | ### Control Comments 95 | # Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for 96 | # the /*@keyword@*/ control comments and JScript conditional comments. (The latter is 97 | # enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason, 98 | # although legacy control comments are enabled by default for backward compatibility. 99 | # 100 | -legacy_control_comments 101 | 102 | 103 | ### Defining identifiers 104 | # By default, "option explicit" is enabled on a per-file basis. 105 | # To enable this for all files, use "+always_use_option_explicit" 106 | -always_use_option_explicit 107 | 108 | # Define certain identifiers of which the lint is not aware. 109 | # (Use this in conjunction with the "undeclared identifier" warning.) 110 | # 111 | # Common uses for webpages might be: 112 | +define __dirname 113 | +define clearInterval 114 | +define clearTimeout 115 | +define console 116 | +define exports 117 | +define global 118 | +define module 119 | +define process 120 | +define require 121 | +define setInterval 122 | +define setImmediate 123 | +define setTimeout 124 | +define Buffer 125 | +define JSON 126 | +define Math 127 | 128 | ### JavaScript Version 129 | # To change the default JavaScript version: 130 | #+default-type text/javascript;version=1.5 131 | #+default-type text/javascript;e4x=1 132 | 133 | ### Files 134 | # Specify which files to lint 135 | # Use "+recurse" to enable recursion (disabled by default). 136 | # To add a set of files, use "+process FileName", "+process Folder\Path\*.js", 137 | # or "+process Folder\Path\*.htm". 138 | # 139 | 140 | -------------------------------------------------------------------------------- /tools/jsstyle.conf: -------------------------------------------------------------------------------- 1 | indent=2 2 | doxygen 3 | unparenthesized-return=0 4 | blank-after-start-comment=0 --------------------------------------------------------------------------------