generated from actions/typescript-action
-
Notifications
You must be signed in to change notification settings - Fork 260
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #58 from dorny/unquoted-shell-escape
Improved listing of matching files with `list-files: shell` and `list-files: escape` options
- Loading branch information
Showing
7 changed files
with
126 additions
and
47 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
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,16 +1,57 @@ | ||
import shellEscape from '../src/shell-escape' | ||
import {escape, shellEscape} from '../src/shell-escape' | ||
|
||
test('simple path escaped', () => { | ||
expect(shellEscape('file')).toBe("'file'") | ||
}) | ||
describe('escape() backslash escapes every character except subset of definitely safe characters', () => { | ||
test('simple filename should not be modified', () => { | ||
expect(escape('file.txt')).toBe('file.txt') | ||
}) | ||
|
||
test('path with space is wrapped with single quotes', () => { | ||
expect(shellEscape('file with space')).toBe("'file with space'") | ||
}) | ||
test('directory separator should be preserved and not escaped', () => { | ||
expect(escape('path/to/file.txt')).toBe('path/to/file.txt') | ||
}) | ||
|
||
test('spaces should be escaped with backslash', () => { | ||
expect(escape('file with space')).toBe('file\\ with\\ space') | ||
}) | ||
|
||
test('path with quote is divided into quoted segments and escaped quote', () => { | ||
expect(shellEscape("file'with quote")).toBe("'file'\\''with quote'") | ||
test('quotes should be escaped with backslash', () => { | ||
expect(escape('file\'with quote"')).toBe('file\\\'with\\ quote\\"') | ||
}) | ||
|
||
test('$variables should be escaped', () => { | ||
expect(escape('$var')).toBe('\\$var') | ||
}) | ||
}) | ||
test('path with leading quote does not have double quotes at beginning', () => { | ||
expect(shellEscape("'file-leading-quote")).toBe("\\''file-leading-quote'") | ||
|
||
describe('shellEscape() returns human readable filenames with as few escaping applied as possible', () => { | ||
test('simple filename should not be modified', () => { | ||
expect(shellEscape('file.txt')).toBe('file.txt') | ||
}) | ||
|
||
test('directory separator should be preserved and not escaped', () => { | ||
expect(shellEscape('path/to/file.txt')).toBe('path/to/file.txt') | ||
}) | ||
|
||
test('filename with spaces should be quoted', () => { | ||
expect(shellEscape('file with space')).toBe("'file with space'") | ||
}) | ||
|
||
test('filename with spaces should be quoted', () => { | ||
expect(shellEscape('file with space')).toBe("'file with space'") | ||
}) | ||
|
||
test('filename with $ should be quoted', () => { | ||
expect(shellEscape('$var')).toBe("'$var'") | ||
}) | ||
|
||
test('filename with " should be quoted', () => { | ||
expect(shellEscape('file"name')).toBe("'file\"name'") | ||
}) | ||
|
||
test('filename with single quote should be wrapped in double quotes', () => { | ||
expect(shellEscape("file'with quote")).toBe('"file\'with quote"') | ||
}) | ||
|
||
test('filename with single quote and special characters is split and quoted/escaped as needed', () => { | ||
expect(shellEscape("file'with $quote")).toBe("file\\''with $quote'") | ||
}) | ||
}) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,28 @@ | ||
// Credits to https://github.com/xxorax/node-shell-escape | ||
// Backslash escape every character except small subset of definitely safe characters | ||
export function escape(value: string): string { | ||
return value.replace(/([^a-zA-Z0-9,._+:@%/-])/gm, '\\$1') | ||
} | ||
|
||
// Returns filename escaped for usage as shell argument. | ||
// Applies "human readable" approach with as few escaping applied as possible | ||
export function shellEscape(value: string): string { | ||
if (value === '') return value | ||
|
||
// Only safe characters | ||
if (/^[a-zA-Z0-9,._+:@%/-]+$/m.test(value)) { | ||
return value | ||
} | ||
|
||
if (value.includes("'")) { | ||
// Only safe characters, single quotes and white-spaces | ||
if (/^[a-zA-Z0-9,._+:@%/'\s-]+$/m.test(value)) { | ||
return `"${value}"` | ||
} | ||
|
||
// Split by single quote and apply escaping recursively | ||
return value.split("'").map(shellEscape).join("\\'") | ||
} | ||
|
||
export default function shellEscape(value: string): string { | ||
return `'${value.replace(/'/g, "'\\''")}'` | ||
.replace(/^(?:'')+/g, '') // unduplicate single-quote at the beginning | ||
.replace(/\\'''/g, "\\'") // remove non-escaped single-quote if there are enclosed between 2 escaped | ||
// Contains some unsafe characters but no single quote | ||
return `'${value}'` | ||
} |