Skip to content

New in PowerShell 7.3: Expressions are evaluated but aren't substituted with the results #18668

Closed

Description

@conioh

Prerequisites

Steps to reproduce

In certain contexts PowerShell 7.3 evaluates expressions (i.e. $(Do-Something)) but doesn't replace the expressions with the results of the evaluation.

Accepted behaviors are:

However evaluating an expressing and not performing substitution is unacceptable.

Evaluating expressions in contexts where they are not replaced makes as much sense - that is, none - as the second line in the following code actually destroying the computer:

PS C:\> "$(Get-Location)"
C:\
PS C:\> '$(Destroy-Computer)'
$(Destroy-Computer)
PS C:\>

This is wrong, dangerous and unacceptable.

This issue is related to #18502 but distinct from it.
That issue deals with commas preventing substitution.
Regardless of whether not performing substitution is appropriate in contexts other than verbatim strings and whether arguments that contains commas to external commands is one of those context, in no case whatsoever should expressions that are not substituted be evaluated.

Since the verbal explanation in that issue wasn't clear enough to elicit the proper response I have also prepared a visual:

a

Expected behavior

PS C:\> #$PSNativeCommandArgumentPassing = "Legacy"
PS C:\> cmd /c echo foo,cd=$(Write-Host "completely insane"; "return")
completely insane
foo,cd=return

Actual behavior

PS C:\> cmd /c echo foo,cd=$(Write-Host "completely insane"; "return")
completely insane
foo,cd=$(Write-Host"completelyinsane";"return")
PS C:\>

Error details

No response

Environment data

PS C:\> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.3.0
PSEdition                      Core
GitCommitId                    7.3.0
OS                             Microsoft Windows 10.0.22621
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

PS C:\>

Visuals

No response

Activity

  1. added
    Needs-TriageThe issue is new and needs to be triaged by a work group.
    on Nov 28, 2022
  2. 237dmitry commented on Nov 28, 2022

    @237dmitry

    Screenshot 2022-11-28 160407

  3. conioh commented on Nov 28, 2022

    @conioh
    Author

    Screenshot 2022-11-28 160407

    Did you not see the part titled "environment data"?

  4. kilasuit commented on Nov 28, 2022

    @kilasuit
    Collaborator

    conioh your steps to reproduce could have been a little clearer

    however 237dmitry was showing that when you hadn't commented this out $PSNativeCommandArgumentPassing = "Legacy" prior to running cmd /c echo foo,cd=$(Write-Host "completely insane"; "return") that you do indeed get the desired result as can be shown in this screenshot on my system

    image

  5. 237dmitry commented on Nov 28, 2022

    @237dmitry

    Did you not see the part titled "environment data"?

    My Environment Data is the same.

  6. kilasuit commented on Nov 28, 2022

    @kilasuit
    Collaborator

    237dmitry - I thought it would be but to many a prompt with $ in it is often seen to be a unix like system.

  7. 237dmitry commented on Nov 28, 2022

    @237dmitry

    I thought it would be but to many a prompt with $ in it is often seen to be a unix like system.

    What does it matter?

    Screenshot 2022-11-28 172416

  8. conioh commented on Nov 28, 2022

    @conioh
    Author

    conioh your steps to reproduce could have been a little clearer

    Ryan Yates (@kilasuit), what's unclear? Please explain and I will attempt to improve the report.

    however 237dmitry was showing that when you hadn't commented this out $PSNativeCommandArgumentPassing = "Legacy" prior to running cmd /c echo foo,cd=$(Write-Host "completely insane"; "return") that you do indeed get the desired result as can be shown in this screenshot on my system

    So if you perform different actions than those I have described you get different results?
    That's hardly surprising and I don't see even a bit of relevance.

    There are lots of other ways to "get the desired result" if you think the result is a string printed to the console:

    PS C:\> Write-Host "foo,cd=return"
    foo,cd=return

    This too "get[s] the desired result", but only with a malicious interpretation of the desired result.

    The example is an example. The issue is "expressions are evaluated but aren't substituted with the results", not "I am unable to send to the standard output the string foo,cd=return".
    Finding a different way to print that string is irrelevant. The issue is that PowerShell evaluates an expression where it shouldn't.

  9. mklement0 commented on Nov 28, 2022

    @mklement0
    Contributor

    To provide some context:

    • To me, this issue is a duplicate of PowerShell 7.3 doesn't pass variable values / cmdlet results arguments to child processes when comma is present #18502 (which is also mentioned in the initial post).

      • That the behavior exhibited there (and elaborated on here) should never occur is a given, and doesn't need discussion as a general hypothetical.

        • Granted, the only official response to the linked bug report so far was bewilderingly cavalier, but I don't think anyone would actually argue that the behavior is intentional.
      • By contrast, if there are other actual bugs where this behavior manifests, they should be called out specifically, in their own bug reports.

    • The behavior at hand is clearly a bug, and it was introduced in the context of introducing $PSNativeCommandArgumentPassing in 7.3.0

      • Expansion in the given scenario should always occur, irrespective of the effective $PSNativeCommandArgumentPassing value.
  10. conioh commented on Nov 28, 2022

    @conioh
    Author

    My Environment Data is the same.

    237dmitry, no offence but I don't believe you.

    A few minutes later:

    Screenshot 2022-11-28 172416

    I see that I was right.
    On Windows the default value for $PSNativeCommandArgumentPassing is "Windows". If you somehow changed it you're not using the same environment and if you did that intentionally that's dishonest.

    Obviously I did not specify in the bug report "don't run any other command before the one I provided" and I also did not specify "use and empty PowerShell profile file" because that's implied in any bug report.

    Perhaps your Profile.ps1 file executes Enter-PSSession and everything entered after that happens in a remote computer against PowerShell 7.0? There's no way to anticipate all of this and it is generally understood that this is not to be done.

  11. mklement0 commented on Nov 28, 2022

    @mklement0
    Contributor

    The issue is that PowerShell evaluates an expression where it shouldn't.

    In this case it actually should evaluate the expression - as it always has - and therefore, of course, also perform expansion (i.e. replace the subexpression with its (stringified) evaluation result in the argument); the latter part is now broken.

  12. conioh commented on Nov 28, 2022

    @conioh
    Author

    Michael Klement (@mklement0), I believe this not to be a duplicate for the reason states at the bottom of the report:

    This issue is related to #18502 but distinct from it.
    That issue deals with commas preventing substitution.
    Regardless of whether not performing substitution is appropriate in contexts other than verbatim strings and whether arguments that contains commas to external commands is one of those context, in no case whatsoever should expressions that are not substituted be evaluated.

    These are two separate issues:

    They are distinct and independent. It is possible to fix this issue by not evaluating the expression without fixing the other one and having commas cause the odd treatment of command-line arguments.

    Edit:

    In this case it actually should evaluate the expression - as it always has - and therefore, of course, also perform expansion (i.e. replace the subexpression with its (stringified) evaluation result in the argument); the latter part is now broken.

    I'm trying to be realistic here. The clever folks here perhaps really have decided that:

    There's specialized code added for this new behavior (fixing a different issue) which treats arguments with commas slightly different than before. Variables are no longer expanded when they appear with commas.

    But even given that decision the evaluation is wrong.

  13. jhoneill commented on Nov 28, 2022

    @jhoneill

    Ryan Yates (@kilasuit)

    conioh your steps to reproduce could have been a little clearer

    It took me multiple readings are true

    however 237dmitry was showing that when you hadn't commented this out $PSNativeCommandArgumentPassing = "Legacy" prior to running cmd /c echo foo,cd=$(Write-Host "completely insane"; "return") that you do indeed get the desired result as can be shown in this screenshot on my system

    Yes. When legacy is set "return" and not "$(Write-Host "completely insane"; "return")" is passed to the command all good.
    When it is not set $(Write-Host "completely insane"; "return") is sent. That is also expected
    What is not expected is as well as sending the literal string, to the command, any evaluation still happens. In other words

    cmd /c echo foo,cd=$(Write-Host "completely insane"; "return")
    first does what $() tells it to do and gets the value of what is inside the brackets. conioh Write-Host doesn't write to standard output, it bypasses std-out and goes to the console. So we do a write host then send some text back.
    Now: if the value is set to legacy return is sent,
    but if the value is set to Windows $(Write-Host "completely insane"; "return") is sent,

    Evaluating what's inside $() and sending the result makes sense.
    Not evaluating what's inside and sending $(blah blah) also makes sense
    Evaluating what's inside and the sending $(blah blah) is at best wildly counter intutitve, and as Michael Klement (@mklement0) has just said while I've been typing, looks like a bug.

  14. mklement0 commented on Nov 28, 2022

    @mklement0
    Contributor

    conioh

    The issues are the same, because the symptom only arises in the context of passing arguments to external programs, and it is an unwanted side effect of how argument-passing to external programs has changed in 7.3.0 (, is not a metacharacter when passing arguments to external programs, and should not cause any modification in behavior - an unquoted argument containing , should be treated like any other implicit expandable string).

    Again, if you know of specific other contexts where a similar symptom manifests, please create separate reports.

    The comment by James Truher (@JamesWTruher) you link to (which is the one I also linked to and called "bewilderingly cavalier") should not be construed as a decision on how to handle your bug report - and in this particular case I certainly hope that the behavior will ultimately be recognized for the - serious - regression bug that it is.

    In short:

  15. mklement0 commented on Nov 28, 2022

    @mklement0
    Contributor

    James O'Neill (@jhoneill)

    When it [$PSNativeCommandArgumentPassing = 'Legacy'] is not set, $(Write-Host "completely insane"; "return") is sent. That is also expected

    No - $PSNativeCommandArgumentPassing is unrelated to expansion behavior, so its value shouldn't matter.

    Write-Host doesn't write to standard output

    I think conioh is aware of that and deliberately used Write-Host to demonstrate that despite the non-expansion, evaluation still took place - which never makes sense and can be dangerous.

  16. conioh commented on Nov 28, 2022

    @conioh
    Author

    Michael Klement (@mklement0), the actionable thing is to immediately stop evaluating expressions in those comma-containing command-line arguments contexts while we consider whether commas should affect argument parsing at all and try to find out what JamesWTruher meant, what is the mysterious bug he referred to etc.

    I agree with you that the answer to this should be "no, commas should not affect this", but while that issue it being investigated this issue should be immediately fixed.

  17. kilasuit commented on Nov 28, 2022

    @kilasuit
    Collaborator

    conioh - the team is predominantly US West coast based so only recently having got up and gotten online - so I hope that Steve Lee (@SteveL-MSFT) and team will look into this in the coming hours & hope that we will see a PR that can fix this soon enough

    But please be realistic on timeframes as immediately just isn't possible here

  18. 237dmitry commented on Nov 28, 2022

    @237dmitry

    I think you misunderstand Legacy mode. It allows you to avoid escaping service symbols that could be interpreted by PowerShell as a part of its own syntax. But that doesn't preclude the use of variables. If you want to get the command line as it is, you must use the --% operator.

    Linux:

    ArcoLinux_2022-11-28_21-06-18

    The last command via --% outputs empty string because $string is not bash variable.

  19. conioh commented on Nov 28, 2022

    @conioh
    Author

    Ryan Yates (@kilasuit), I didn't mean "immediately" to be understood as "within the hour" but more like "as soon as reasonably possible". PowerShell 7.3.1 is a good place for this fix. PowerShell 7.4 is bad.

  20. jhoneill commented on Nov 28, 2022

    @jhoneill

    James O'Neill (@jhoneill)

    When it [$PSNativeCommandArgumentPassing = 'Legacy'] is not set, $(Write-Host "completely insane"; "return") is sent. That is also expected

    No - $PSNativeCommandArgumentPassing is unrelated to expansion behavior, so its value shouldn't matter.

    I think you've quoted the wrong half of what I said. If it is set to legacy the expanded value is sent., if not the expansion still happens (because as you say PSNativeCommandArgumentPassing doesn't stop expansion happening) but the expanded result isn't sent. Which is hard to rationalize.

    Write-Host doesn't write to standard output

    I think conioh is aware of that and deliberately used Write-Host to demonstrate that despite the non-expansion, evaluation still took place - which never makes sense and can be dangerous.

    It was a comment made about std output

  21. mklement0 commented on Nov 28, 2022

    @mklement0
    Contributor

    I'm not sure if there's still a disagreement, and maybe this is just about words, but to be clear:

    if not the expansion still happens (because as you say PSNativeCommandArgumentPassing doesn't stop expansion happening)

    Expansion - meaning replacing the source code of the subexpression in the original, raw argument with the result of its evaluation - is not happening in the case at hand - only evaluation is (with the (success-stream-output) result getting discarded).

    And it is the lack of expansion that constitutes the bug.

    It was a comment made about std output

    As an aside: I think it's better to use the term success (output) stream, so as to distinguish it from stdout, which only applies to external programs.

    My comment was meant to point out the intentional bypassing of the success-output stream with Write-Host, as simple proof that evaluation happened.

  22. conioh commented on Nov 28, 2022

    @conioh
    Author

    Michael Klement (@mklement0), I mentioned "standard output" in passing in another comment, not in the root post and that's what James O'Neill (@jhoneill) referred to.
    I thought it was immaterial to the matter at hand so I didn't reply to James O'Neill (@jhoneill)'s comment about it and just fixed that comment.
    But you're both technically right that Write-Host writes directly to the console (regardless of stream redirection) and that PowerShell has multiple output streams (success/error/debug/...) and not just stdout and stderr.
    I fixed that so we can move on.

    Regarding the substantial issue here I think we're basically in agreement.

  23. jhoneill commented on Nov 29, 2022

    @jhoneill

    Michael Klement (@mklement0) yes, reading this last night my brain was swapping "expand" (the string) and "evaluate" (the expression) freely.

    The expression is being evaluated, (it runs) whether or not the string is expanded (text of expression replaced by its result as a string). I think we've all got that.

  24. microsoft-github-policy-service commented on Nov 15, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  25. microsoft-github-policy-service commented on Nov 15, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  26. microsoft-github-policy-service commented on Nov 15, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  27. microsoft-github-policy-service commented on Nov 22, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs-TriageThe issue is new and needs to be triaged by a work group.Resolution-No ActivityIssue has had no activity for 6 months or more

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions