Loading devtools/client/netmonitor/test/browser_net_copy_as_curl.js +2 −2 Original line number Diff line number Diff line Loading @@ -16,7 +16,7 @@ add_task(async function () { info("Starting test... "); // Different quote chars are used for Windows and POSIX const QUOTE_WIN = '"'; const QUOTE_WIN = '^"'; const QUOTE_POSIX = "'"; const isWin = Services.appinfo.OS === "WINNT"; Loading Loading @@ -219,7 +219,7 @@ async function testForPlatform(tab, monitor, testData) { // This monster regexp parses the command line into an array of arguments, // recognizing quoted args with matching quotes and escaped quotes inside: // [ "curl 'url'", "--standalone-arg", "-arg-with-quoted-string 'value\'s'" ] const matchRe = /[-A-Za-z1-9]+(?: ([\"'])(?:\\\1|.)*?\1)?/g; const matchRe = /[-A-Za-z1-9]+(?: ([\^\\"']+)(?:\\\1|.)*?\1)?/g; const actual = result.match(matchRe); // Must begin with the same "curl 'URL'" segment Loading devtools/client/netmonitor/test/browser_net_curl-utils.js +20 −20 Original line number Diff line number Diff line Loading @@ -153,7 +153,7 @@ function testDataArgumentOnGeneratedCommand(data) { } function testDataEscapeOnGeneratedCommand(data) { const paramsWin = `--data-raw "{""param1"":""value1"",""param2"":""value2""}"`; const paramsWin = `--data-raw ^"{\\"param1\\":\\"value1\\",\\"param2\\":\\"value2\\"}^"`; const paramsPosix = `--data-raw '{"param1":"value1","param2":"value2"}'`; let curlCommand = Curl.generateCommand(data, "WINNT"); Loading Loading @@ -206,22 +206,22 @@ function testRemoveBinaryDataFromMultipartText(data) { ].join(""); const EXPECTED_WIN_RESULT = [ '"', '^"', boundary, '"^\u000d\u000A\u000d\u000A"', 'Content-Disposition: form-data; name=""param1""', '"^\u000d\u000A\u000d\u000A""^\u000d\u000A\u000d\u000A"', "^\u000A\u000A", 'Content-Disposition: form-data; name=\\"param1\\"', "^\u000A\u000A^\u000A\u000A", "value1", '"^\u000d\u000A\u000d\u000A"', "^\u000A\u000A", boundary, '"^\u000d\u000A\u000d\u000A"', 'Content-Disposition: form-data; name=""file""; filename=""filename.png""', '"^\u000d\u000A\u000d\u000A"', "^\u000A\u000A", 'Content-Disposition: form-data; name=\\"file\\"; filename=\\"filename.png\\"', "^\u000A\u000A", "Content-Type: image/png", '"^\u000d\u000A\u000d\u000A""^\u000d\u000A\u000d\u000A"', "^\u000A\u000A^\u000A\u000A", boundary + "--", '"^\u000d\u000A\u000d\u000A"', '"', "^\u000A\u000A", '^"', ].join(""); if (Services.appinfo.OS != "WINNT") { Loading Loading @@ -316,56 +316,56 @@ function testEscapeStringWin() { const surroundedWithDoubleQuotes = "A simple string"; is( CurlUtils.escapeStringWin(surroundedWithDoubleQuotes), '"A simple string"', '^"A simple string^"', "The string should be surrounded with double quotes." ); const doubleQuotes = 'Quote: "Time is an illusion. Lunchtime doubly so."'; is( CurlUtils.escapeStringWin(doubleQuotes), '"Quote: ""Time is an illusion. Lunchtime doubly so."""', '^"Quote: \\"Time is an illusion. Lunchtime doubly so.\\"^"', "Double quotes should be escaped." ); const percentSigns = "%TEMP% %@foo% %2XX% %_XX% %?XX%"; is( CurlUtils.escapeStringWin(percentSigns), '"^%^TEMP^% ^%^@foo^% ^%^2XX^% ^%^_XX^% ^%?XX^%"', '^"^%^TEMP^% ^%^@foo^% ^%^2XX^% ^%^_XX^% ^%?XX^%^"', "Percent signs should be escaped." ); const backslashes = "\\A simple string\\"; is( CurlUtils.escapeStringWin(backslashes), '"\\\\A simple string\\\\"', '^"\\\\A simple string\\\\^"', "Backslashes should be escaped." ); const newLines = "line1\r\nline2\r\rline3\n\nline4"; is( CurlUtils.escapeStringWin(newLines), '"line1"^\r\n\r\n"line2"^\r\n\r\n""^\r\n\r\n"line3"^\r\n\r\n""^\r\n\r\n"line4"', '^"line1^\n\nline2\r\rline3^\n\n^\n\nline4^"', "Newlines should be escaped." ); const dollarSignCommand = "$(calc.exe)"; is( CurlUtils.escapeStringWin(dollarSignCommand), '"\\$(calc.exe)"', '^"\\$(calc.exe)^"', "Dollar sign should be escaped." ); const tickSignCommand = "`$(calc.exe)"; is( CurlUtils.escapeStringWin(tickSignCommand), '"\\`\\$(calc.exe)"', '^"\\`\\$(calc.exe)^"', "Both the tick and dollar signs should be escaped." ); const evilCommand = `query=evil\r\rcmd" /c timeout /t 3 & calc.exe\r\r`; is( CurlUtils.escapeStringWin(evilCommand), '"query=evil"^\r\n\r\n""^\r\n\r\n"cmd"" /c timeout /t 3 & calc.exe"^\r\n\r\n""^\r\n\r\n""', '^"query=evil\r\rcmd\\" /c timeout /t 3 & calc.exe\r\r^"', "The evil command is escaped properly" ); } Loading devtools/client/shared/curl.js +36 −36 Original line number Diff line number Diff line Loading @@ -58,17 +58,11 @@ const Curl = { generateCommand(data, platform) { const utils = CurlUtils; let command = ["curl"]; let commandParts = []; // Make sure to use the following helpers to sanitize arguments before execution. const addParam = value => { const safe = /^[a-zA-Z-]+$/.test(value) ? value : escapeString(value); command.push(safe); }; const addPostData = value => { const safe = /^[a-zA-Z-]+$/.test(value) ? value : escapeString(value); postData.push(safe); const escapeStringifNeeded = value => { return /^[a-zA-Z-]+$/.test(value) ? value : escapeString(value); }; const ignoredHeaders = new Set(); Loading @@ -77,17 +71,17 @@ const Curl = { // The cURL command is expected to run on the same platform that Firefox runs // (it may be different from the inspected page platform). const escapeString = currentPlatform == "WINNT" currentPlatform === "WINNT" ? utils.escapeStringWin : utils.escapeStringPosix; // Add URL. addParam(data.url); commandParts.push(escapeString(data.url)); // Disable globbing if the URL contains brackets. // cURL also globs braces but they are already percent-encoded. if (data.url.includes("[") || data.url.includes("]")) { addParam("--globoff"); commandParts.push("--globoff"); } let postDataText = null; Loading @@ -104,13 +98,13 @@ const Curl = { // which composed using \n only, not \r\n, may be not parsable for // peers which split parts of multipart payload using \r\n. postDataText = data.postDataText; addPostData("--data-binary"); postData.push("--data-binary"); const boundary = utils.getMultipartBoundary(data); const text = utils.removeBinaryDataFromMultipartText( postDataText, boundary ); addPostData(text); postData.push(escapeStringifNeeded(text)); ignoredHeaders.add("content-length"); } else if ( data.postDataText && Loading @@ -119,8 +113,10 @@ const Curl = { ) { // When no postData exists, --data-raw should not be set postDataText = data.postDataText; addPostData("--data-raw"); addPostData(utils.writePostDataTextParams(postDataText)); postData.push( "--data-raw " + escapeStringifNeeded(`${utils.writePostDataTextParams(postDataText)}`) ); ignoredHeaders.add("content-length"); } // curl generates the host header itself based on the given URL Loading @@ -128,20 +124,19 @@ const Curl = { // Add --compressed if the response is compressed if (utils.isContentEncodedResponse(data)) { addParam("--compressed"); commandParts.push("--compressed"); } // Add -I (HEAD) // For servers that supports HEAD. // This will fetch the header of a document only. if (data.method === "HEAD") { addParam("-I"); commandParts.push("-I"); } else if (data.method !== "GET") { // Add method. // For HEAD and GET requests this is not necessary. GET is the // default, -I implies HEAD. addParam("-X"); addParam(data.method); commandParts.push("-X " + escapeStringifNeeded(`${data.method}`)); } // Add request headers. Loading @@ -155,14 +150,26 @@ const Curl = { if (ignoredHeaders.has(header.name.toLowerCase())) { continue; } addParam("-H"); addParam(header.name + ": " + header.value); commandParts.push( "-H " + escapeStringifNeeded(`${header.name}: ${header.value}`) ); } // Add post data. command = command.concat(postData); return command.join(" "); commandParts = commandParts.concat(postData); // Format with line breaks if the command has more than 2 parts // e.g // Command with 2 parts - curl https://foo.com // Commands with more than 2 parts - // curl https://foo.com // -X POST // -H "Accept : */*" // -H "accept-language: en-US" const joinStr = currentPlatform === "WINNT" ? " ^\n " : " \\\n "; return ( "curl " + commandParts.join(commandParts.length >= 3 ? joinStr : " ") ); }, }; Loading Loading @@ -447,18 +454,16 @@ const CurlUtils = { same escape characters, they can interact with each other in horrible ways, the order of operations is critical. */ const encapsChars = '"'; const encapsChars = '^"'; return ( encapsChars + str // Replace \ with \\ first because it is an escape character for certain // conditions in both parsers. .replace(/\\/g, "\\\\") // Replace double quote chars with two double quotes (not by escaping with \") because it is // recognized by both cmd.exe and MS Crt arguments parser. .replace(/"/g, '""') // Escape double quotes with double slashes. .replace(/"/g, '\\"') // Escape ` and $ so commands do not get executed e.g $(calc.exe) or `\$(calc.exe) .replace(/[`$]/g, "\\$&") Loading @@ -476,15 +481,10 @@ const CurlUtils = { // by the previous replace. .replace(/%(?=[a-zA-Z0-9_])/g, "%^") // We replace \r and \r\n with \n, this allows to consistently escape all new // lines in the next replace .replace(/\r\n?/g, "\n") // Lastly we replace new lines with ^ and TWO new lines because the first // new line is there to enact the escape command the second is the character // to escape (in this case new line). // The extra " enables escaping new lines with ^ within quotes in cmd.exe. .replace(/\n/g, '"^\r\n\r\n"') + .replace(/\r?\n/g, "^\n\n") + encapsChars ); }, Loading devtools/client/shared/test/xpcshell/test_curl.js +11 −6 Original line number Diff line number Diff line Loading @@ -227,9 +227,10 @@ add_task(async function () { // Check binary data const dataBinaryPos = cmd.indexOf("--data-binary"); const dataBinaryParam = `--data-binary ${isWin() ? "" : "$"}${escapeNewline( const dataBinaryParam = `--data-binary ${isWin() ? "^\n " : "\\\n $"}${escapeNewline( quote(request.postDataText) )}`; ok(dataBinaryPos !== -1, "--data-binary param present in curl output"); equal( cmd.substr(dataBinaryPos, dataBinaryParam.length), Loading Loading @@ -332,13 +333,15 @@ function isWin() { return Services.appinfo.OS === "WINNT"; } const QUOTE = isWin() ? '"' : "'"; const QUOTE = isWin() ? '^"' : "'"; // Quote a string, escape the quotes inside the string function quote(str) { let escaped; if (isWin()) { escaped = str.replace(new RegExp(QUOTE, "g"), `${QUOTE}${QUOTE}`); escaped = str .replace(new RegExp(QUOTE, "g"), `${QUOTE}${QUOTE}`) .replace(/"/g, '\\"'); } else { escaped = str.replace(new RegExp(QUOTE, "g"), `\\${QUOTE}`); } Loading @@ -347,8 +350,10 @@ function quote(str) { function escapeNewline(txt) { if (isWin()) { // Add `"` to close quote, then escape newline outside of quote, then start new quote return txt.replace(/[\r\n]{1,2}/g, '"^$&$&"'); // Replace new lines with ^ and TWO new lines because the first // new line is there to enact the escape command the second is the character // to escape (in this case new line). return txt.replace(/\r?\n/g, "^\n\n"); } return txt.replace(/\r/g, "\\r").replace(/\n/g, "\\n"); } Loading Loading @@ -384,6 +389,6 @@ function parseCurl(curlCmd) { // This monster regexp parses the command line into an array of arguments, // recognizing quoted args with matching quotes and escaped quotes inside: // [ "curl 'url'", "--standalone-arg", "-arg-with-quoted-string 'value\'s'" ] const matchRe = /[-A-Za-z1-9]+(?: \$?([\"'])(?:\\\1|.)*?\1)?/g; const matchRe = /[-A-Za-z1-9]+(?: ([\^\\"']+)(?:\\\1|.)*?\1)?/g; return curlCmd.match(matchRe); } Loading
devtools/client/netmonitor/test/browser_net_copy_as_curl.js +2 −2 Original line number Diff line number Diff line Loading @@ -16,7 +16,7 @@ add_task(async function () { info("Starting test... "); // Different quote chars are used for Windows and POSIX const QUOTE_WIN = '"'; const QUOTE_WIN = '^"'; const QUOTE_POSIX = "'"; const isWin = Services.appinfo.OS === "WINNT"; Loading Loading @@ -219,7 +219,7 @@ async function testForPlatform(tab, monitor, testData) { // This monster regexp parses the command line into an array of arguments, // recognizing quoted args with matching quotes and escaped quotes inside: // [ "curl 'url'", "--standalone-arg", "-arg-with-quoted-string 'value\'s'" ] const matchRe = /[-A-Za-z1-9]+(?: ([\"'])(?:\\\1|.)*?\1)?/g; const matchRe = /[-A-Za-z1-9]+(?: ([\^\\"']+)(?:\\\1|.)*?\1)?/g; const actual = result.match(matchRe); // Must begin with the same "curl 'URL'" segment Loading
devtools/client/netmonitor/test/browser_net_curl-utils.js +20 −20 Original line number Diff line number Diff line Loading @@ -153,7 +153,7 @@ function testDataArgumentOnGeneratedCommand(data) { } function testDataEscapeOnGeneratedCommand(data) { const paramsWin = `--data-raw "{""param1"":""value1"",""param2"":""value2""}"`; const paramsWin = `--data-raw ^"{\\"param1\\":\\"value1\\",\\"param2\\":\\"value2\\"}^"`; const paramsPosix = `--data-raw '{"param1":"value1","param2":"value2"}'`; let curlCommand = Curl.generateCommand(data, "WINNT"); Loading Loading @@ -206,22 +206,22 @@ function testRemoveBinaryDataFromMultipartText(data) { ].join(""); const EXPECTED_WIN_RESULT = [ '"', '^"', boundary, '"^\u000d\u000A\u000d\u000A"', 'Content-Disposition: form-data; name=""param1""', '"^\u000d\u000A\u000d\u000A""^\u000d\u000A\u000d\u000A"', "^\u000A\u000A", 'Content-Disposition: form-data; name=\\"param1\\"', "^\u000A\u000A^\u000A\u000A", "value1", '"^\u000d\u000A\u000d\u000A"', "^\u000A\u000A", boundary, '"^\u000d\u000A\u000d\u000A"', 'Content-Disposition: form-data; name=""file""; filename=""filename.png""', '"^\u000d\u000A\u000d\u000A"', "^\u000A\u000A", 'Content-Disposition: form-data; name=\\"file\\"; filename=\\"filename.png\\"', "^\u000A\u000A", "Content-Type: image/png", '"^\u000d\u000A\u000d\u000A""^\u000d\u000A\u000d\u000A"', "^\u000A\u000A^\u000A\u000A", boundary + "--", '"^\u000d\u000A\u000d\u000A"', '"', "^\u000A\u000A", '^"', ].join(""); if (Services.appinfo.OS != "WINNT") { Loading Loading @@ -316,56 +316,56 @@ function testEscapeStringWin() { const surroundedWithDoubleQuotes = "A simple string"; is( CurlUtils.escapeStringWin(surroundedWithDoubleQuotes), '"A simple string"', '^"A simple string^"', "The string should be surrounded with double quotes." ); const doubleQuotes = 'Quote: "Time is an illusion. Lunchtime doubly so."'; is( CurlUtils.escapeStringWin(doubleQuotes), '"Quote: ""Time is an illusion. Lunchtime doubly so."""', '^"Quote: \\"Time is an illusion. Lunchtime doubly so.\\"^"', "Double quotes should be escaped." ); const percentSigns = "%TEMP% %@foo% %2XX% %_XX% %?XX%"; is( CurlUtils.escapeStringWin(percentSigns), '"^%^TEMP^% ^%^@foo^% ^%^2XX^% ^%^_XX^% ^%?XX^%"', '^"^%^TEMP^% ^%^@foo^% ^%^2XX^% ^%^_XX^% ^%?XX^%^"', "Percent signs should be escaped." ); const backslashes = "\\A simple string\\"; is( CurlUtils.escapeStringWin(backslashes), '"\\\\A simple string\\\\"', '^"\\\\A simple string\\\\^"', "Backslashes should be escaped." ); const newLines = "line1\r\nline2\r\rline3\n\nline4"; is( CurlUtils.escapeStringWin(newLines), '"line1"^\r\n\r\n"line2"^\r\n\r\n""^\r\n\r\n"line3"^\r\n\r\n""^\r\n\r\n"line4"', '^"line1^\n\nline2\r\rline3^\n\n^\n\nline4^"', "Newlines should be escaped." ); const dollarSignCommand = "$(calc.exe)"; is( CurlUtils.escapeStringWin(dollarSignCommand), '"\\$(calc.exe)"', '^"\\$(calc.exe)^"', "Dollar sign should be escaped." ); const tickSignCommand = "`$(calc.exe)"; is( CurlUtils.escapeStringWin(tickSignCommand), '"\\`\\$(calc.exe)"', '^"\\`\\$(calc.exe)^"', "Both the tick and dollar signs should be escaped." ); const evilCommand = `query=evil\r\rcmd" /c timeout /t 3 & calc.exe\r\r`; is( CurlUtils.escapeStringWin(evilCommand), '"query=evil"^\r\n\r\n""^\r\n\r\n"cmd"" /c timeout /t 3 & calc.exe"^\r\n\r\n""^\r\n\r\n""', '^"query=evil\r\rcmd\\" /c timeout /t 3 & calc.exe\r\r^"', "The evil command is escaped properly" ); } Loading
devtools/client/shared/curl.js +36 −36 Original line number Diff line number Diff line Loading @@ -58,17 +58,11 @@ const Curl = { generateCommand(data, platform) { const utils = CurlUtils; let command = ["curl"]; let commandParts = []; // Make sure to use the following helpers to sanitize arguments before execution. const addParam = value => { const safe = /^[a-zA-Z-]+$/.test(value) ? value : escapeString(value); command.push(safe); }; const addPostData = value => { const safe = /^[a-zA-Z-]+$/.test(value) ? value : escapeString(value); postData.push(safe); const escapeStringifNeeded = value => { return /^[a-zA-Z-]+$/.test(value) ? value : escapeString(value); }; const ignoredHeaders = new Set(); Loading @@ -77,17 +71,17 @@ const Curl = { // The cURL command is expected to run on the same platform that Firefox runs // (it may be different from the inspected page platform). const escapeString = currentPlatform == "WINNT" currentPlatform === "WINNT" ? utils.escapeStringWin : utils.escapeStringPosix; // Add URL. addParam(data.url); commandParts.push(escapeString(data.url)); // Disable globbing if the URL contains brackets. // cURL also globs braces but they are already percent-encoded. if (data.url.includes("[") || data.url.includes("]")) { addParam("--globoff"); commandParts.push("--globoff"); } let postDataText = null; Loading @@ -104,13 +98,13 @@ const Curl = { // which composed using \n only, not \r\n, may be not parsable for // peers which split parts of multipart payload using \r\n. postDataText = data.postDataText; addPostData("--data-binary"); postData.push("--data-binary"); const boundary = utils.getMultipartBoundary(data); const text = utils.removeBinaryDataFromMultipartText( postDataText, boundary ); addPostData(text); postData.push(escapeStringifNeeded(text)); ignoredHeaders.add("content-length"); } else if ( data.postDataText && Loading @@ -119,8 +113,10 @@ const Curl = { ) { // When no postData exists, --data-raw should not be set postDataText = data.postDataText; addPostData("--data-raw"); addPostData(utils.writePostDataTextParams(postDataText)); postData.push( "--data-raw " + escapeStringifNeeded(`${utils.writePostDataTextParams(postDataText)}`) ); ignoredHeaders.add("content-length"); } // curl generates the host header itself based on the given URL Loading @@ -128,20 +124,19 @@ const Curl = { // Add --compressed if the response is compressed if (utils.isContentEncodedResponse(data)) { addParam("--compressed"); commandParts.push("--compressed"); } // Add -I (HEAD) // For servers that supports HEAD. // This will fetch the header of a document only. if (data.method === "HEAD") { addParam("-I"); commandParts.push("-I"); } else if (data.method !== "GET") { // Add method. // For HEAD and GET requests this is not necessary. GET is the // default, -I implies HEAD. addParam("-X"); addParam(data.method); commandParts.push("-X " + escapeStringifNeeded(`${data.method}`)); } // Add request headers. Loading @@ -155,14 +150,26 @@ const Curl = { if (ignoredHeaders.has(header.name.toLowerCase())) { continue; } addParam("-H"); addParam(header.name + ": " + header.value); commandParts.push( "-H " + escapeStringifNeeded(`${header.name}: ${header.value}`) ); } // Add post data. command = command.concat(postData); return command.join(" "); commandParts = commandParts.concat(postData); // Format with line breaks if the command has more than 2 parts // e.g // Command with 2 parts - curl https://foo.com // Commands with more than 2 parts - // curl https://foo.com // -X POST // -H "Accept : */*" // -H "accept-language: en-US" const joinStr = currentPlatform === "WINNT" ? " ^\n " : " \\\n "; return ( "curl " + commandParts.join(commandParts.length >= 3 ? joinStr : " ") ); }, }; Loading Loading @@ -447,18 +454,16 @@ const CurlUtils = { same escape characters, they can interact with each other in horrible ways, the order of operations is critical. */ const encapsChars = '"'; const encapsChars = '^"'; return ( encapsChars + str // Replace \ with \\ first because it is an escape character for certain // conditions in both parsers. .replace(/\\/g, "\\\\") // Replace double quote chars with two double quotes (not by escaping with \") because it is // recognized by both cmd.exe and MS Crt arguments parser. .replace(/"/g, '""') // Escape double quotes with double slashes. .replace(/"/g, '\\"') // Escape ` and $ so commands do not get executed e.g $(calc.exe) or `\$(calc.exe) .replace(/[`$]/g, "\\$&") Loading @@ -476,15 +481,10 @@ const CurlUtils = { // by the previous replace. .replace(/%(?=[a-zA-Z0-9_])/g, "%^") // We replace \r and \r\n with \n, this allows to consistently escape all new // lines in the next replace .replace(/\r\n?/g, "\n") // Lastly we replace new lines with ^ and TWO new lines because the first // new line is there to enact the escape command the second is the character // to escape (in this case new line). // The extra " enables escaping new lines with ^ within quotes in cmd.exe. .replace(/\n/g, '"^\r\n\r\n"') + .replace(/\r?\n/g, "^\n\n") + encapsChars ); }, Loading
devtools/client/shared/test/xpcshell/test_curl.js +11 −6 Original line number Diff line number Diff line Loading @@ -227,9 +227,10 @@ add_task(async function () { // Check binary data const dataBinaryPos = cmd.indexOf("--data-binary"); const dataBinaryParam = `--data-binary ${isWin() ? "" : "$"}${escapeNewline( const dataBinaryParam = `--data-binary ${isWin() ? "^\n " : "\\\n $"}${escapeNewline( quote(request.postDataText) )}`; ok(dataBinaryPos !== -1, "--data-binary param present in curl output"); equal( cmd.substr(dataBinaryPos, dataBinaryParam.length), Loading Loading @@ -332,13 +333,15 @@ function isWin() { return Services.appinfo.OS === "WINNT"; } const QUOTE = isWin() ? '"' : "'"; const QUOTE = isWin() ? '^"' : "'"; // Quote a string, escape the quotes inside the string function quote(str) { let escaped; if (isWin()) { escaped = str.replace(new RegExp(QUOTE, "g"), `${QUOTE}${QUOTE}`); escaped = str .replace(new RegExp(QUOTE, "g"), `${QUOTE}${QUOTE}`) .replace(/"/g, '\\"'); } else { escaped = str.replace(new RegExp(QUOTE, "g"), `\\${QUOTE}`); } Loading @@ -347,8 +350,10 @@ function quote(str) { function escapeNewline(txt) { if (isWin()) { // Add `"` to close quote, then escape newline outside of quote, then start new quote return txt.replace(/[\r\n]{1,2}/g, '"^$&$&"'); // Replace new lines with ^ and TWO new lines because the first // new line is there to enact the escape command the second is the character // to escape (in this case new line). return txt.replace(/\r?\n/g, "^\n\n"); } return txt.replace(/\r/g, "\\r").replace(/\n/g, "\\n"); } Loading Loading @@ -384,6 +389,6 @@ function parseCurl(curlCmd) { // This monster regexp parses the command line into an array of arguments, // recognizing quoted args with matching quotes and escaped quotes inside: // [ "curl 'url'", "--standalone-arg", "-arg-with-quoted-string 'value\'s'" ] const matchRe = /[-A-Za-z1-9]+(?: \$?([\"'])(?:\\\1|.)*?\1)?/g; const matchRe = /[-A-Za-z1-9]+(?: ([\^\\"']+)(?:\\\1|.)*?\1)?/g; return curlCmd.match(matchRe); }