Python调用Powershell的Invoke-RestMethod,将Json作为字符串传递到Body中。

huangapple go评论73阅读模式
英文:

Python call Powershell Invoke-RestMethod with Json as string into Body

问题

import requests
import subprocess
import base64

# 定义凭证
credentials = "login:password"
# 定义URL
url = f'urlXXXurl'
# 定义请求体
body = '{"id": 4986986, "key": "2df534ee-270b-4ab4-83fb-1b308febacce", ...}'

# 构建请求头
headers = f'@{{Authorization = \'Basic %s\'}}' % base64.b64encode(credentials.encode("ascii")).decode("ascii")
# 构建PowerShell命令
command = f'powershell.exe Invoke-RestMethod -Method Post -Uri %s -ContentType application/json -Body "{body.replace("\"", "\\\"")}" -Headers (Invoke-Expression {headers}) -UseBasicParsing' % (url)
# 执行命令
response = subprocess.check_call(command)
英文:
import requests
import subprocess
import base64


credentials = "login:password"
url = f'urlXXXurl'
body = '{"id": 4986986, "key": "2df534ee-270b-4ab4-83fb-1b308febacce", ...}'


headers = '"""@{Authorization = \'Basic %s\'}"""' % base64.b64encode(credentials.encode("ascii")).decode("ascii")
command = 'powershell.exe Invoke-RestMethod -Method Post -Uri %s -ContentType application/json -Body """%s""" -Headers ( Invoke-Expression %s ) -UseBasicParsing' % (url, body.replace('"', '\"'), headers)
response = subprocess.check_call(command)

Is there some kind of necessary conversion, so that JSON would be recognizable by PowerShell?

答案1

得分: 1

最后我使用了这种方法:

command = "powershell.exe ConvertTo-Json (Invoke-RestMethod -Method Post -Uri %s -ContentType application/json -Body '%s' -Headers (Invoke-Expression %s) -UseBasicParsing)" \
                      % (url, json.dumps(self.asset).replace('"', '""""'), headers)
英文:

Finally I used this approach

command = "powershell.exe ConvertTo-Json ( Invoke-RestMethod -Method Post -Uri %s -ContentType application/json -Body '%s' -Headers ( Invoke-Expression %s ) -UseBasicParsing )" \
                      % (url, json.dumps(self.asset).replace('"', '"""'), headers)

答案2

得分: 1

以下是您要翻译的内容:

  • 对于您问题中的方法,唯一的即时问题是在 -Body 参数周围使用了 """..."""(从PowerShell的角度看,这将导致 -Command 参数中的 "..." 字符串):

    • 这样做需要两种转义技巧:\(或 """)以便保留任何 " 字符作为传递给PowerShell CLI的(隐含的) -Command 参数的一部分,并且 ` 转义 " 字符,嵌入在值中的 ...

    • 因此,尽管您可以使用 body.replace('"', '`\"')(请注意 `),但使用 '...' 更简单,[1] 因为 ' 在命令行上不需要转义,并允许嵌入 " 字符(尽管通过CLI作为 \")。

  • 无需使用 ( Invoke-Expression %s ),因为您可以直接构建表示 -Header 参数的字符串作为PowerShell 散列表 文字。

  • 正如Mathias所指出的,如果要使命令输出JSON字符串,请使用 Invoke-WebRequest 并访问输出对象的 .Content 属性。

    • 相比之下,如果使用 Invoke-RestMethod,PowerShell会自动且无条件地将JSON响应解析为对象图(这就是在 您自己的回答 中调用 ConvertTo-Json 的原因,如果使用 (Invoke-WebRequest).Content 则不需要)。

将上述内容应用于您的代码,使用Python v3.6+ f-strings:

import requests
import subprocess
import base64

credentials = 'login:password'
url = 'urlXXXurl'
body = '{"id": 4986986, "key": "2df534ee-270b-4ab4-83fb-1b308febacce", ...}'

# 构建表示PowerShell散列表文字的标头字符串。
# 请注意,在f字符串中使用{{和}}来使用*字面上*的{和}字符。
headers = f"@{{Authorization = 'Basic {base64.b64encode(credentials.encode('ascii')).decode('ascii')}'}}"

# 转义JSON中的"字符为\",
# 以便PowerShell CLI不会去掉它们。
bodyEscaped = body.replace('\"', '\"')

# 注意:
# 由于使用了'...' PowerShell字符串,假设
# 标头或bodyEscaped本身不包含'字符
# 如果包含,这些嵌入的'必须转义为''
command = f'''
powershell.exe -NoProfile -Command
  (
    Invoke-WebRequest -Uri '{url}' -Body '{bodyEscaped}' -Headers {headers} -Method Post -ContentType application/json -UseBasicParsing
  ).Content
'''

response = subprocess.check_call(command)

请注意,[Windows PowerShell CLI](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_powershell_exe)-NoProfile参数和-Command参数在这里并不是严格必需的:

  • -NoProfile绕过了配置文件(初始化文件)的加载,因此可能更快,并且使执行环境更可预测。
  • -Command-c)如果没有使用,将被隐含使用,但出于概念上的清晰性考虑,它被包含在内(尤其是因为 pwsh,即_PowerShell (Core)_ CLI,现在要求使用-Command,因为它默认为-File)。

最后,请注意,从技术上讲,PowerShell命令作为多个参数传递,而不是作为一个字符串传递,不加(未转义的)"..."

  • 如果(另一个)shell,尤其是cmd.exe,涉及到调用(在subprocess.call()中没有涉及到),则建议使用"...",以防止该shell意外解释字符,如&|
  • 另外,如果您的PowerShell命令中有嵌入的字符串,其中包含需要保留为多个空格的运行的情况(不使用"..."封闭,这些运行将折叠为单个空格)。
英文:
  • The only immediate problem with the approach in your question was the use of """...""" around the -Body argument (resulting in a "..." string from PowerShell's perspective):

    • Doing so requires two escaping techniques: \ (or """) in order to preserve any " characters as part of the PowerShell command passed to the the (implied) -Command parameter of PowerShell's CLI and `-escaping of the " chars. embedded in the value (...).

    • While you could therefore have used body.replace(&#39;&quot;&#39;, &#39;`\&quot;&#39;) (note the `), it is simpler to use &#39;...&#39; for strings,<sup>[1]</sup> given that &#39; does
      not require escaping on the command line and allows embedding &quot; characters as-is (albeit as \&quot; via the CLI).

  • There is no need for ( Invoke-Expression %s ), given that you can formulate the string representing the -Header argument directly as a PowerShell hashtable literal.

  • As Mathias points out, if you want the command to output a JSON string, use Invoke-WebRequest and access the output object's .Content property.

    • By contrast, if you use Invoke-RestMethod, PowerShell automatically and invariably parses a JSON response into an object graph (which is what necessitated the ConvertTo-Json call in your own answer, which isn't needed if you use (Invoke-WebRequest).Content).

Applying the above to your code, using Python v3.6+ f-strings:

import requests
import subprocess
import base64

credentials = &#39;login:password&#39;
url = &#39;urlXXXurl&#39;
body = &#39;{&quot;id&quot;: 4986986, &quot;key&quot;: &quot;2df534ee-270b-4ab4-83fb-1b308febacce&quot;, ...}&#39;

# Construct the header as a string representing a PowerShell hashtable literal.
# Note the the use of {{ and }} to use *literal* { and } chars. in the f-string.
headers = f&quot;@{{Authorization = &#39;Basic {base64.b64encode(credentials.encode(&#39;ascii&#39;)).decode(&#39;ascii&#39;)}&#39;}}&quot;

# Escape the &quot; chars. in JSON as \&quot;, 
# so that the PowerShell CLI doesn&#39;t strip them.
bodyEscaped = body.replace(&#39;&quot;&#39;, &#39;\&quot;&#39;)

# Note:
#  Due to use of &#39;...&#39; PowerShell strings, the assumption is that
#  neither header nor bodyEscaped themselves contain &#39;
#  If they did, these embedded &#39; would have to be escaped as &#39;&#39;
command = f&quot;&quot;&quot;\
powershell.exe -NoProfile -Command
  (
    Invoke-WebRequest -Uri &#39;{url}&#39; -Body &#39;{bodyEscaped}&#39; -Headers {headers} -Method Post -ContentType application/json -UseBasicParsing
  ).Content
&quot;&quot;&quot;

response = subprocess.check_call(command)

Note that neither the -NoProfile nor the -Command parameter of the Windows PowerShell CLI are strictly needed here:

  • -NoProfile bypasses profile (initialization-file) loading and is therefore both potentially faster and makes for a more predictable execution environment.
  • -Command (-c) would be implied if not used, but is included for conceptual clarity (not least because pwsh, the PowerShell (Core) CLI, now requires -Command, as it defaults to -File).

Finally, note that - for simplicity - what constitutes the PowerShell command is technically passed as multiple arguments rather than as a single string enclosed in (unescaped) &quot;...&quot;.

  • If (another) shell, notably cmd.exe, were involved in the call (it isn't with subprocess.call()), use of &quot;...&quot; would be advisable, to prevent accidental interpretation of characters such as &amp; and | by that shell.
  • Separately, &quot;...&quot; would also be needed in the rare event that your PowerShell command has embedded strings with runs of multiple spaces that need to be preserved as such (without &quot;...&quot; enclosure, such runs would be folded into a single space each).

<sup>[1] This assumes that you don't need PowerShell's string interpolation, which requires &quot;...&quot; strings.</sup>

huangapple
  • 本文由 发表于 2023年7月13日 22:03:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76680271.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定