-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unable to pass & in an argument to a shell script started with Process.start #59604
Comments
@brianquinlan I'm told that you might be knowledgeable in this area :-) |
Summary: User cannot escape |
You can make this test pass by doing This is also a reason why See also this SO question for gnarly details. |
Thank you! I actually wondered if it was related to So there's no bug here, and we'll probably have to double-escape these for now then (generally we know we are going through Flutter... it might break if users configure a It would be nice to come up with a better solution (like compiling a non- (FYI @bkonyi) |
@mraleph I'm not sure if I may have still found a bug.. interested in your opinion. Firstly, I decided that maybe it's better to escape inside So I have an updated test script below which writes the escaping into the shell script (in reality this would go into
It seems to work for every character currently in the escape set (the test passes).. however, if I add a double quote to the test characters, it fails because the output contains import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
final charactersToEscape = r'^&|<>\';
// Adding double quote " into the set of the test characters breaks, because
// we get back ^" even though we don't escape a double quote anywhere.
const testCharacters = r'abc ^&|<>\:%=+-_@~#<>?/';
final testArgs = [
for (var char in testCharacters.split('')) 'before${char}after',
];
void main() {
test('test escaping', () async {
var tempDir =
Directory.systemTemp.createTempSync('flutter dap args escape test');
print('Writing scripts to $tempDir');
// Write a shell script to simulate the Flutter .bat file and a Dart script
// that just prints the arguments sent to it one-per-line.
var tempShellScript = File(path.join(tempDir.path, 'fake_flutter.bat'));
var tempDartScript = File(path.join(tempDir.path, 'print_args.dart'));
var shellScriptContents = '''
@echo off
SET "args=%*"
SETLOCAL EnableDelayedExpansion
${charactersToEscape.split('').map((c) => 'SET "args=!args:$c=^$c!"').join('\n')}
"${Platform.resolvedExecutable}" "${tempDartScript.path}" %args%
''';
tempShellScript.writeAsStringSync(shellScriptContents);
tempDartScript.writeAsStringSync(r'''
void main(List<String> args) {
print(args.join('\n'));
}
''');
var executable = tempShellScript.path;
var args = testArgs.map(_escapeAndQuoteArg).toList();
print('''
Executing:
executable: $executable
args: ${args.join(' ')}
runInShell: true
''');
var proc = await Process.start(
'"$executable"',
args,
);
var stdoutFuture = proc.stdout.transform(utf8.decoder).toList();
var stderrFuture = proc.stderr.transform(utf8.decoder).toList();
await proc.exitCode;
var stdout = (await stdoutFuture).join().trim();
var stderr = (await stderrFuture).join().trim();
if (stderr.isNotEmpty) {
print(stderr);
}
var actual = stdout
.split('\n')
.map((l) => l.trim())
.where((l) => l.isNotEmpty)
.toList();
expect(actual, testArgs);
});
}
String _escapeAndQuoteArg(String input) {
final escapedChars = RegExp.escape(charactersToEscape);
return input.replaceAllMapped(
new RegExp('[$escapedChars]'), (m) => '^${m.group(0)}');
} |
FWIW some variations involving delayed expansion were tried before but it caused its own bunch of problems, see flutter/flutter#84270 I am also not sure you can just really escape things reliably like that.
Yeah, there is some escaping in As a result of that escaping you get |
I'm trying to fix a bug where we mishandle some characters like
&
in user-provided arguments because of having to spawnflutter.bat
through a shell. There is a little info onProcess.start()
about this:The issue I'm having is that I can't find any way to escape a
&
that comes through correctly.Below is a script to reproduce the issue. It creates a temp folder with a space in the name, and then writes a simple
.bat
file that forwards all args to a Dart script (similar to whatflutter.bat
does). The Dart script it writes just prints all the arguments out to stdout. The test reads stdout and compares what's printed to what it sent as arguments to ensure they match.If I remove the
&
fromtestCharacters
, the test passes. If I add the&
then it fails because the last argument is truncated, and it tried to executeafter
:The
_escapeAndQuoteArg
function needs to escape&
in some way, but I've tried many combinations (including backslashes, the^
character and combinations of quoting/not quoting the args) (I'm assuming https://ss64.com/nt/syntax-esc.html is a reasonable source), but none of them work. Based on @derekxu16 comment at #50076 (comment) it's not clear to me if Dart is also trying to do some of this escaping.I'm not sure if this is a bug, or I'm doing it wrong. I'm hoping someone that understands the code in
createProcess
may be able to verify one way or the other.The text was updated successfully, but these errors were encountered: