-
-
Notifications
You must be signed in to change notification settings - Fork 203
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Better escaping of
result.escapedCommand
- Loading branch information
Showing
6 changed files
with
149 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,64 @@ | ||
import {platform} from 'node:process'; | ||
|
||
export const joinCommand = (filePath, rawArgs) => { | ||
const fileAndArgs = [filePath, ...rawArgs]; | ||
const command = fileAndArgs.join(' '); | ||
const escapedCommand = fileAndArgs.map(arg => escapeArg(arg)).join(' '); | ||
const escapedCommand = fileAndArgs.map(arg => quoteString(escapeControlCharacters(arg))).join(' '); | ||
return {command, escapedCommand}; | ||
}; | ||
|
||
const escapeArg = arg => typeof arg === 'string' && !NO_ESCAPE_REGEXP.test(arg) | ||
? `"${arg.replaceAll('"', '\\"')}"` | ||
: arg; | ||
const escapeControlCharacters = arg => typeof arg === 'string' | ||
? arg.replaceAll(SPECIAL_CHAR_REGEXP, character => escapeControlCharacter(character)) | ||
: String(arg); | ||
|
||
const escapeControlCharacter = character => { | ||
const commonEscape = COMMON_ESCAPES[character]; | ||
if (commonEscape !== undefined) { | ||
return commonEscape; | ||
} | ||
|
||
const codepoint = character.codePointAt(0); | ||
const codepointHex = codepoint.toString(16); | ||
return codepoint <= ASTRAL_START | ||
? `\\u${codepointHex.padStart(4, '0')}` | ||
: `\\U${codepointHex}`; | ||
}; | ||
|
||
// Characters that would create issues when printed are escaped using the \u or \U notation. | ||
// Those include control characters and newlines. | ||
// The \u and \U notation is Bash specific, but there is no way to do this in a shell-agnostic way. | ||
// Some shells do not even have a way to print those characters in an escaped fashion. | ||
// Therefore, we prioritize printing those safely, instead of allowing those to be copy-pasted. | ||
// List of Unicode character categories: https://www.fileformat.info/info/unicode/category/index.htm | ||
const SPECIAL_CHAR_REGEXP = /[\p{Separator}\p{Control}\p{Format}\p{Surrogate}\p{Private_Use}]/gu; | ||
|
||
// Accepted by $'...' in Bash. | ||
// Exclude \a \e \v which are accepted in Bash but not in JavaScript (except \v) and JSON. | ||
const COMMON_ESCAPES = { | ||
' ': ' ', | ||
'\b': '\\b', | ||
'\f': '\\f', | ||
'\n': '\\n', | ||
'\r': '\\r', | ||
'\t': '\\t', | ||
}; | ||
|
||
// Up until that codepoint, \u notation can be used instead of \U | ||
const ASTRAL_START = 65_535; | ||
|
||
// Some characters are shell-specific, i.e. need to be escaped when the command is copy-pasted then run. | ||
// Escaping is shell-specific. We cannot know which shell is used: `process.platform` detection is not enough. | ||
// For example, Windows users could be using `cmd.exe`, Powershell or Bash for Windows which all use different escaping. | ||
// We use '...' on Unix, which is POSIX shell compliant and escape all characters but ' so this is fairly safe. | ||
// On Windows, we assume cmd.exe is used and escape with "...", which also works with Powershell. | ||
const quoteString = escapedArg => { | ||
if (NO_ESCAPE_REGEXP.test(escapedArg)) { | ||
return escapedArg; | ||
} | ||
|
||
return platform === 'win32' | ||
? `"${escapedArg.replaceAll('"', '""')}"` | ||
: `'${escapedArg.replaceAll('\'', '\'\\\'\'')}'`; | ||
}; | ||
|
||
const NO_ESCAPE_REGEXP = /^[\w.-]+$/; | ||
const NO_ESCAPE_REGEXP = /^[\w./-]+$/; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters