Skip to content
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

Improve PowerShell command and argument escaping #1611

Merged
merged 5 commits into from
Nov 3, 2021
Merged

Improve PowerShell command and argument escaping #1611

merged 5 commits into from
Nov 3, 2021

Conversation

JustinGrote
Copy link
Collaborator

@JustinGrote JustinGrote commented Nov 1, 2021

Fixes #1608

@rjmholt please ensure that changing '' to backtick-quote is what was intended, it looked like a bug to me that was causing the triple-quotes.

@SeeminglyScience @andschwa Per my last PR, maybe we are being "overly helpful" with the escaping of the arguments and should instead throw errors to encourage editors to fix their problems at the source?

For instance in a launch.json, specifying a parameter like this is a clear mistake, but this tool will accept it, escape it, and maybe produce a difficult-to-troubleshoot result:

args: "-Path","I'putaquoteinthewrongplaceaccidentally"

@andyleejordan
Copy link
Member

andyleejordan commented Nov 1, 2021

Really appreciating the tests 🥳

@andyleejordan andyleejordan self-requested a review November 1, 2021 18:43
@JustinGrote
Copy link
Collaborator Author

JustinGrote commented Nov 1, 2021

I'm gonna rewrite this to be less aggressive on the argument escaping.

For instance, if someone has a launch.json that is "","arg1","arg2", we should allow expressions to be used.

e.g. "Path/to/my/script.ps1" "-Path" "$ENV:MyCustomEnvPath" will not work with the current escaping I've set up

I think very minimal escaping should be done for these parameters, it doesn't provide any defense really because I can just code inject into the "command" .ps1 script
If you need the character explicitly you can still backtick it out e.g. "-Path" "I need a `' literal quote here"

@JustinGrote JustinGrote marked this pull request as ready for review November 2, 2021 04:26
@JustinGrote
Copy link
Collaborator Author

@andschwa the first parameter in the launch config is now always quoted, which will work for both path and command arguments. It passes all tests and all vscode extension tests currently (I removed the pathing escape tests which are no longer relevant).

Copy link
Collaborator

@SeeminglyScience SeeminglyScience left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lookin' good :) couple of questions and suggestions

{
sb.Append(arg);
}
sb
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: consider,

sb.Append(' ').Append(ArgumentEscaping.Escape(arg));

Personally I like using text as the variable for a string builder to enable this:

text.Append(' ')
    .Append(ArgumentEscaping.Escape(arg));

Very minor.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was matching style of other things in PSES, we can certainly do this if @andschwa doesn't mind.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally fine. I'm not super aggressive about style, I think anytime a style question is in play it should be addressed by tooling rather than back and forth on PRs. Do what you think is readable 😄

case '`':
sb.Append('`').Append(curr);
break;
var wildcardEscapedPath = WildcardPattern.Escape(path);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May need to manually handle ` still due to PowerShell/PowerShell#16306. There is a good chance that this seemingly over engineered logic was here due to issue with that API. Unsure when it was added though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably do need to escape it to handle a situation where it exists in a file name at least at the command level, will probably leave it alone at an argument level

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#123 is the relevant reasoning, I don't think we need to escape every path that comes in, this should be more handled on the output side if required as part of a cmdlet argument, if it's thought the cmdlet will interpret handling characters, because in some cases that may be exactly what the caller wants.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to add a test for SetLineBreakpoint which is the only other place this is referenced.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably do need to escape it to handle a situation where it exists in a file name at least at the command level, will probably leave it alone at an argument level

Yeah it gets tricky with no real way to please everyone here. On one hand you want to enable VSCode variables like ${workspaceFolder} in which case you can't really escape manually, and on the other hand some folks may want to use expressions.

Like you said though for the command path always escape is a clear winner for sure.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SeeminglyScience the compromise for my implementation is that:

  1. Command is always wrapped in double quotes because it is used by the call operator, so ${} expressions will still work if you want to pass a file and not a command, the only file that won't work in this case is if the filename contains $(), I think that's reasonable enough to just say "dont name your files this way", should probably have an explicit exception check for this.
  2. Argument is only wrapped in quotes if you specify a space. If you really need to be literal or use workspacefolder in an arg and have it be safe, you should wrap it in quotes, a scriptblock, or $(). I think that's a good compromise

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SeeminglyScience looks like all the debugserver tests were commented out by @rjmholt after his commit and probably need to be rewritten. @andschwa can you come up with a way to get a debugservice instantiated to a point I can run tests against it? Been trying to figure it out and it's too much for my non-C# brain at the moment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it gets tricky with no real way to please everyone here. On one hand you want to enable VSCode variables like ${workspaceFolder} in which case you can't really escape manually, and on the other hand some folks may want to use expressions.

Wouldn't this be interpolated before it gets to the LSP server? I could be wrong, I just assumed that the Code client would interpolate their own variable expressions, and then send the full string over.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JustinGrote

looks like all the debugserver tests were commented out

I will prioritize this. I really want them up. Thanks for working on this issue!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it gets tricky with no real way to please everyone here. On one hand you want to enable VSCode variables like ${workspaceFolder} in which case you can't really escape manually, and on the other hand some folks may want to use expressions.

Wouldn't this be interpolated before it gets to the LSP server? I could be wrong, I just assumed that the Code client would interpolate their own variable expressions, and then send the full string over.

They do, the problem is that you don't know that they are a path or an interpolation once they arrive to the LSP. I think this implemention is a good compromise.

@JustinGrote JustinGrote changed the title PowershellArgumentNeedsEscaping does not account for a single quoted string PowershellArgumentNeedsEscaping does not account for a single quoted string - MOVED TO 1611 Nov 2, 2021
@JustinGrote JustinGrote changed the title PowershellArgumentNeedsEscaping does not account for a single quoted string - MOVED TO 1611 Better Powershell Command and Argument Escape Handling Nov 2, 2021
@JustinGrote
Copy link
Collaborator Author

JustinGrote commented Nov 3, 2021

I built a .vsix of this and it is working for pester test extension, native pester codelens, and all my debug launch.json configurations for the last day or so without issue. It is good as is unless we want to adjust for @SeeminglyScience's nit, and cover the debugservice tests in a separate PR since looks like they all need to be redone anyways.

@andyleejordan
Copy link
Member

cover the debugservice tests in a separate PR since looks like they all need to be redone anyways.

💯 Can merge!

public static string Escape(string Arg)
{
// if argument is a scriptblock return as-is
if (Arg.StartsWith("{") && Arg.EndsWith("}"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JustinGrote Should we strip whitespace first? In case it's { ... } or some other variation with (pointless) spaces before/after.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JustinGrote Should we strip whitespace first? In case it's { ... } or some other variation with (pointless) spaces before/after.

I think this is another case of "overly helpful", if you specified your debug config and left a space at the beginning or the end before your scriptblock, I have to think you may have had an intention there.

@andyleejordan andyleejordan changed the title Better Powershell Command and Argument Escape Handling Improve PowerShell command and argument escaping Nov 3, 2021
@andyleejordan andyleejordan enabled auto-merge (squash) November 3, 2021 21:15
@andyleejordan andyleejordan merged commit 7cc7569 into PowerShell:master Nov 3, 2021
@JustinGrote JustinGrote deleted the 1608 branch November 3, 2021 21:55
@andyleejordan
Copy link
Member

Coming back to to this @JustinGrote and deciding we need to not quote escape args with spaces in them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.

PowershellArgumentNeedsEscaping does not account for a single quoted string
3 participants