Skip to content

Commit 5285ccc

Browse files
committed
implement better shell quoting thanks to @jakubroztocil
1 parent e21bdef commit 5285ccc

30 files changed

+111
-87
lines changed

src/helpers/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict'
2+
3+
module.exports = require('require-directory')(module)
File renamed without changes.

src/helpers/shell/escape.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use strict'
2+
3+
module.exports = function (value) {
4+
return value.replace(/\r/g, '\\r').replace(/\n/g, '\\n')
5+
}

src/helpers/shell/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict'
2+
3+
module.exports = require('require-directory')(module)

src/helpers/shell/quote.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use strict'
2+
3+
var util = require('util')
4+
5+
/**
6+
* Use "strong quoting" using single quotes so that we only need
7+
* to deal with nested single quote characters.
8+
* <http://wiki.bash-hackers.org/syntax/quoting#strong_quoting>
9+
*/
10+
module.exports = function (value) {
11+
var safe = /^[a-z0-9-_/.@%^=:]+$/i
12+
13+
// Unless `value` is a simple shell-safe string, quote it.
14+
if (!safe.test(value)) {
15+
return util.format("'%s'", value.replace(/'/g, "'\\''"))
16+
}
17+
18+
return value
19+
}

src/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ var debug = require('debug')('httpsnippet')
44
var es = require('event-stream')
55
var MultiPartForm = require('form-data')
66
var qs = require('querystring')
7-
var reducer = require('./reducer')
7+
var helpers = require('./helpers')
88
var targets = require('./targets')
99
var url = require('url')
1010
var util = require('util')
@@ -42,7 +42,7 @@ var HTTPSnippet = function (req, lang) {
4242
if (this.source.queryString && this.source.queryString.length) {
4343
debug('queryString found, constructing queryString pair map')
4444

45-
this.source.queryObj = this.source.queryString.reduce(reducer, {})
45+
this.source.queryObj = this.source.queryString.reduce(helpers.reducer, {})
4646
}
4747

4848
// construct headers objects
@@ -96,7 +96,7 @@ var HTTPSnippet = function (req, lang) {
9696
if (!this.source.postData.params) {
9797
this.source.postData.text = ''
9898
} else {
99-
this.source.postData.paramsObj = this.source.postData.params.reduce(reducer, {})
99+
this.source.postData.paramsObj = this.source.postData.params.reduce(helpers.reducer, {})
100100

101101
// always overwrite
102102
this.source.postData.text = qs.stringify(this.source.postData.paramsObj)

src/targets/curl.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

33
var util = require('util')
4+
var shell = require('../helpers/shell')
45

56
module.exports = function (source, options) {
67
var opts = util._extend({
@@ -12,30 +13,32 @@ module.exports = function (source, options) {
1213

1314
code.push(util.format('curl %s %s', opts.short ? '-X' : '--request', source.method))
1415

15-
code.push(util.format('%s"%s"', opts.short ? '' : '--url ', source.fullUrl))
16+
code.push(util.format('%s%s', opts.short ? '' : '--url ', shell.quote(source.fullUrl)))
1617

1718
if (source.httpVersion === 'HTTP/1.0') {
1819
code.push(opts.short ? '-0' : '--http1.0')
1920
}
2021

2122
// construct headers
2223
Object.keys(source.headersObj).sort().map(function (key) {
23-
code.push(util.format('%s "%s: %s"', opts.short ? '-H' : '--header', key, source.headersObj[key]))
24+
var header = util.format('%s: %s', key, source.headersObj[key])
25+
code.push(util.format('%s %s', opts.short ? '-H' : '--header', shell.quote(header)))
2426
})
2527

2628
if (source.allHeaders.cookie) {
27-
code.push(util.format('%s "%s"', opts.short ? '-b' : '--cookie', source.allHeaders.cookie))
29+
code.push(util.format('%s %s', opts.short ? '-b' : '--cookie', shell.quote(source.allHeaders.cookie)))
2830
}
2931

3032
// request body
3133
if (source.postData.text) {
32-
code.push(util.format('%s %s', opts.short ? '-d' : '--data', JSON.stringify(source.postData.text)))
34+
code.push(util.format('%s %s', opts.short ? '-d' : '--data', shell.escape(shell.quote(source.postData.text))))
3335
}
3436

3537
// construct post params
3638
if (!source.postData.text && source.postData.params) {
3739
source.postData.params.map(function (param) {
38-
code.push(util.format('%s "%s=%s"', opts.short ? '-F' : '--form', param.name, param.value))
40+
var post = util.format('%s=%s', param.name, param.value)
41+
code.push(util.format('%s %s', opts.short ? '-F' : '--form', shell.quote(post)))
3942
})
4043
}
4144

src/targets/httpie.js

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,7 @@
11
'use strict'
22

33
var util = require('util')
4-
5-
var shellQuote = function (value) {
6-
// Unless `value` is a simple shell-safe string, quote it.
7-
var shellSafe = /^[a-z0-9-_/.@%^=:]+$/i
8-
if (!shellSafe.test(value)) {
9-
// Use "strong quoting" using single quotes so that we only need
10-
// to deal with nested single quote characters.
11-
// <http://wiki.bash-hackers.org/syntax/quoting#strong_quoting>
12-
return util.format("'%s'", value.replace(/'/g, "'\\''"))
13-
}
14-
return value
15-
}
4+
var quote = require('../helpers/shell/quote')
165

176
module.exports = function (source, options) {
187
var opts = util._extend({
@@ -34,7 +23,7 @@ module.exports = function (source, options) {
3423

3524
// start with body pipe
3625
if (source.postData && source.postData.text) {
37-
code.push(util.format('echo %s | ', shellQuote(source.postData.text)))
26+
code.push(util.format('echo %s | ', quote(source.postData.text)))
3827
}
3928

4029
var flags = []
@@ -75,7 +64,7 @@ module.exports = function (source, options) {
7564
flags.push(util.format('--timeout=%s', opts.timeout))
7665
}
7766

78-
code.push(util.format('http %s%s %s', flags.length ? flags.join(' ') + ' ' : '', source.method, shellQuote(opts.queryParams ? source.url : source.fullUrl)))
67+
code.push(util.format('http %s%s %s', flags.length ? flags.join(' ') + ' ' : '', source.method, quote(opts.queryParams ? source.url : source.fullUrl)))
7968

8069
// construct query params
8170
if (opts.queryParams) {
@@ -86,23 +75,23 @@ module.exports = function (source, options) {
8675

8776
if (util.isArray(value)) {
8877
value.map(function (val) {
89-
code.push(util.format('%s==%s', name, shellQuote(val)))
78+
code.push(util.format('%s==%s', name, quote(val)))
9079
})
9180
} else {
92-
code.push(util.format('%s==%s', name, shellQuote(value)))
81+
code.push(util.format('%s==%s', name, quote(value)))
9382
}
9483
})
9584
}
9685

9786
// construct headers
9887
Object.keys(source.allHeaders).sort().map(function (key) {
99-
code.push(util.format('%s:%s', key, shellQuote(source.allHeaders[key])))
88+
code.push(util.format('%s:%s', key, quote(source.allHeaders[key])))
10089
})
10190

10291
// construct post params
10392
if (!source.postData.text && source.postData.params && source.postData.params.length) {
10493
source.postData.params.map(function (param) {
105-
code.push(util.format('%s:%s', param.name, shellQuote(param.value)))
94+
code.push(util.format('%s:%s', param.name, quote(param.value)))
10695
})
10796
}
10897

src/targets/wget.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

33
var util = require('util')
4+
var shell = require('../helpers/shell')
45

56
module.exports = function (source, options) {
67
var opts = util._extend({
@@ -17,19 +18,20 @@ module.exports = function (source, options) {
1718
code.push(util.format('wget %s', opts.short ? '-q' : '--quiet'))
1819
}
1920

20-
code.push(util.format('--method %s', source.method))
21+
code.push(util.format('--method %s', shell.quote(source.method)))
2122

2223
Object.keys(source.allHeaders).map(function (key) {
23-
code.push(util.format('--header "%s: %s"', key, source.allHeaders[key]))
24+
var header = util.format('%s: %s', key, source.allHeaders[key])
25+
code.push(util.format('--header %s', shell.quote(header)))
2426
})
2527

2628
if (source.postData.text) {
27-
code.push('--body-data ' + JSON.stringify(source.postData.text))
29+
code.push('--body-data ' + shell.escape(shell.quote(source.postData.text)))
2830
}
2931

3032
code.push(opts.short ? '-O' : '--output-document')
3133

32-
code.push(util.format('- "%s"', source.fullUrl))
34+
code.push(util.format('- %s', shell.quote(source.fullUrl)))
3335

3436
return code.join(opts.indent !== false ? ' \\\n' + opts.indent : ' ')
3537
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
curl --request POST \
2-
--url "http://mockbin.com/har" \
3-
--header "content-type: application/x-www-form-urlencoded" \
4-
--data "foo=bar&hello=world"
2+
--url http://mockbin.com/har \
3+
--header 'content-type: application/x-www-form-urlencoded' \
4+
--data 'foo=bar&hello=world'

0 commit comments

Comments
 (0)