Skip to content

Commit 9899b73

Browse files
committed
Merge branch 'better-escaping'
2 parents 17ce2ef + ebf709c commit 9899b73

File tree

79 files changed

+1418
-125
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+1418
-125
lines changed

package-lock.json

+1-61
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@
8585
"form-data": "3.0.0",
8686
"fs-readfile-promise": "^2.0.1",
8787
"fs-writefile-promise": "^1.0.3",
88-
"har-validator": "^5.0.0",
89-
"stringify-object": "^3.3.0"
88+
"har-validator": "^5.0.0"
9089
}
9190
}

src/helpers/stringify-js-object.js

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Closely based on v5.0.0 of stringify-object (BSD-2-Clause licensed), with
2+
// modifications to simplify it for our use case (simpler obj & regex checks
3+
// + object key retrieval) and no awkwardly incompatible ESM.
4+
5+
module.exports = (input, options, pad) => {
6+
const seen = []
7+
8+
return (function stringify (input, options = {}, pad = '') {
9+
const indent = options.indent || '\t'
10+
11+
let tokens
12+
if (options.inlineCharacterLimit === undefined) {
13+
tokens = {
14+
newline: '\n',
15+
newlineOrSpace: '\n',
16+
pad,
17+
indent: pad + indent
18+
}
19+
} else {
20+
tokens = {
21+
newline: '@@__STRINGIFY_OBJECT_NEW_LINE__@@',
22+
newlineOrSpace: '@@__STRINGIFY_OBJECT_NEW_LINE_OR_SPACE__@@',
23+
pad: '@@__STRINGIFY_OBJECT_PAD__@@',
24+
indent: '@@__STRINGIFY_OBJECT_INDENT__@@'
25+
}
26+
}
27+
28+
const expandWhiteSpace = string => {
29+
if (options.inlineCharacterLimit === undefined) {
30+
return string
31+
}
32+
33+
const oneLined = string
34+
.replace(new RegExp(tokens.newline, 'g'), '')
35+
.replace(new RegExp(tokens.newlineOrSpace, 'g'), ' ')
36+
.replace(new RegExp(tokens.pad + '|' + tokens.indent, 'g'), '')
37+
38+
if (oneLined.length <= options.inlineCharacterLimit) {
39+
return oneLined
40+
}
41+
42+
return string
43+
.replace(new RegExp(tokens.newline + '|' + tokens.newlineOrSpace, 'g'), '\n')
44+
.replace(new RegExp(tokens.pad, 'g'), pad)
45+
.replace(new RegExp(tokens.indent, 'g'), pad + indent)
46+
}
47+
48+
if (seen.includes(input)) {
49+
return '"[Circular]"'
50+
}
51+
52+
if (
53+
input === null ||
54+
input === undefined ||
55+
typeof input === 'number' ||
56+
typeof input === 'boolean' ||
57+
typeof input === 'function' ||
58+
typeof input === 'symbol' ||
59+
input instanceof RegExp
60+
) {
61+
return String(input)
62+
}
63+
64+
if (input instanceof Date) {
65+
return `new Date('${input.toISOString()}')`
66+
}
67+
68+
if (Array.isArray(input)) {
69+
if (input.length === 0) {
70+
return '[]'
71+
}
72+
73+
seen.push(input)
74+
75+
const returnValue = '[' + tokens.newline + input.map((element, i) => {
76+
const eol = input.length - 1 === i ? tokens.newline : ',' + tokens.newlineOrSpace
77+
78+
let value = stringify(element, options, pad + indent)
79+
if (options.transform) {
80+
value = options.transform(input, i, value)
81+
}
82+
83+
return tokens.indent + value + eol
84+
}).join('') + tokens.pad + ']'
85+
86+
seen.pop()
87+
88+
return expandWhiteSpace(returnValue)
89+
}
90+
91+
if (typeof input === 'object') {
92+
let objectKeys = Object.keys(input)
93+
94+
if (options.filter) {
95+
objectKeys = objectKeys.filter(element => options.filter(input, element))
96+
}
97+
98+
if (objectKeys.length === 0) {
99+
return '{}'
100+
}
101+
102+
seen.push(input)
103+
104+
const returnValue = '{' + tokens.newline + objectKeys.map((element, index) => {
105+
const eol = objectKeys.length - 1 === index ? tokens.newline : ',' + tokens.newlineOrSpace
106+
const isSymbol = typeof element === 'symbol'
107+
const isClassic = !isSymbol && /^[a-z$_][$\w]*$/i.test(element)
108+
const key = isSymbol || isClassic ? element : stringify(element, options)
109+
110+
let value = stringify(input[element], options, pad + indent)
111+
if (options.transform) {
112+
value = options.transform(input, element, value)
113+
}
114+
115+
return tokens.indent + String(key) + ': ' + value + eol
116+
}).join('') + tokens.pad + '}'
117+
118+
seen.pop()
119+
120+
return expandWhiteSpace(returnValue)
121+
}
122+
123+
input = input.replace(/\\/g, '\\\\')
124+
input = String(input).replace(/[\r\n]/g, x => x === '\n' ? '\\n' : '\\r')
125+
126+
if (options.singleQuotes === false) {
127+
input = input.replace(/"/g, '\\"')
128+
return `"${input}"`
129+
}
130+
131+
input = input.replace(/'/g, '\\\'')
132+
return `'${input}'`
133+
})(input, options, pad)
134+
}

src/targets/clojure/clj_http.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
const CodeBuilder = require('../../helpers/code-builder')
1414
const helpers = require('../../helpers/headers')
15+
const { escape } = require('../../helpers/format')
1516

1617
const Keyword = function (name) {
1718
this.name = name
@@ -60,7 +61,7 @@ const padBlock = function (x, s) {
6061
const jsToEdn = function (js) {
6162
switch (jsType(js)) {
6263
case 'string':
63-
return '"' + js.replace(/"/g, '\\"') + '"'
64+
return '"' + escape(js, { delimiter: '"' }) + '"'
6465
case 'file':
6566
return js.toString()
6667
case 'keyword':
@@ -73,7 +74,14 @@ const jsToEdn = function (js) {
7374
const obj = Object.keys(js)
7475
.reduce(function (acc, key) {
7576
const val = padBlock(key.length + 2, jsToEdn(js[key]))
76-
return acc + ':' + key + ' ' + val + '\n '
77+
78+
// This check is overly strict, but good enough for us for
79+
// all typical HTTP values we care about
80+
const safeKey = key.match(/^[a-zA-Z_][\w-]*$/)
81+
? ':' + key
82+
: jsToEdn(key)
83+
84+
return acc + safeKey + ' ' + val + '\n '
7785
}, '')
7886
.trim()
7987
return '{' + padBlock(1, obj) + '}'

src/targets/javascript/axios.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
'use strict'
1111

1212
const util = require('util')
13-
const stringifyObject = require('stringify-object')
13+
const stringifyObject = require('../../helpers/stringify-js-object')
1414
const CodeBuilder = require('../../helpers/code-builder')
1515

1616
module.exports = function (source, options) {

src/targets/javascript/fetch.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ module.exports = function (source, options) {
2121
options
2222
)
2323

24-
const stringifyObject = require('stringify-object')
24+
const stringifyObject = require('../../helpers/stringify-js-object')
2525
const code = new CodeBuilder(opts.indent)
2626

2727
options = {
@@ -85,7 +85,7 @@ module.exports = function (source, options) {
8585
.blank()
8686
}
8787

88-
code.push("fetch('%s', options)", source.fullUrl)
88+
code.push('fetch(%s, options)', stringifyObject(source.fullUrl))
8989
.push(1, '.then(response => response.json())')
9090
.push(1, '.then(response => console.log(response))')
9191
.push(1, '.catch(err => console.error(err));')

src/targets/node/axios.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
'use strict'
1111

1212
const util = require('util')
13-
const stringifyObject = require('stringify-object')
13+
const stringifyObject = require('../../helpers/stringify-js-object')
1414
const CodeBuilder = require('../../helpers/code-builder')
1515

1616
module.exports = function (source, options) {

src/targets/node/fetch.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
'use strict'
1212

13-
const stringifyObject = require('stringify-object')
13+
const stringifyObject = require('../../helpers/stringify-js-object')
1414
const CodeBuilder = require('../../helpers/code-builder')
1515

1616
module.exports = function (source, options) {
@@ -88,7 +88,7 @@ module.exports = function (source, options) {
8888
}
8989
}
9090
code.blank()
91-
code.push('let url = \'' + url + '\';')
91+
code.push('let url = ' + stringifyObject(url) + ';')
9292
.blank()
9393
code.push('let options = %s;', stringifyObject(reqOpts, { indent: ' ', inlineCharacterLimit: 80 }))
9494
.blank()

src/targets/node/native.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
'use strict'
1212

13-
const stringifyObject = require('stringify-object')
13+
const stringifyObject = require('../../helpers/stringify-js-object')
1414
const CodeBuilder = require('../../helpers/code-builder')
1515

1616
module.exports = function (source, options) {

src/targets/node/request.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
'use strict'
1212

1313
const util = require('util')
14-
const stringifyObject = require('stringify-object')
14+
const stringifyObject = require('../../helpers/stringify-js-object')
1515
const CodeBuilder = require('../../helpers/code-builder')
1616

1717
module.exports = function (source, options) {

src/targets/objc/helpers.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
const util = require('util')
44

5+
const { escape } = require('../../helpers/format')
6+
57
module.exports = {
68
/**
79
* Create an string of given length filled with blank spaces
@@ -73,7 +75,7 @@ module.exports = {
7375
if (value === null || value === undefined) {
7476
return ''
7577
}
76-
return '@"' + value.toString().replace(/"/g, '\\"') + '"'
78+
return '@"' + escape(value.toString(), { delimiter: '"' }) + '"'
7779
}
7880
}
7981
}

0 commit comments

Comments
 (0)