Comments inline, and another solution with some "special" guard rails for
drag-and-drop specifically.
"wofat68" <atlanticcoast2006@xxxxxx> wrote in message
news:8a96d697-8c8c-433c-a09e-80bc9f94fa38@xxxxxx
> On Aug 1, 4:11 pm, "Alex K. Angelopoulos" <aka(at)mvps.org> wrote:
>
>
>> @echo off
>> :: pswrapper.cmd
>> echo.\script.ps1 %* | powershell -NoExit -Command - >
> After some testing, I found that this does not work on some strings,
> unfortunately. If the argument does *not* contain any spaces, it isn't
> quoted by CMD.EXE. This results in a problem, if the file name
> contains parenthesis. Obviously, PS is trying to interpret that as an
> expression. Yes, that wasn't universal. In any case, you can still encounter problems
even with doublequotes, since several characters that are allowed in file
and folder names can cause serious problems to PowerShell - (){}[]$`' are
all legal.
> This may be also a security problem, since code could be injected.
>
> I just wanted to let you know.
> The other solution I have goes back to the through-argument passing, but I'm
not sure I see this. If anything was being piped in, it could theoretically
expose something, but since the batch wrapper itself invokes the
interactively-specified data explicitly as arguments, you should get no more
and no less vulnerability than you have to injection via the script
arguments.
This did suggest another point to me, however. Since this is explicitly
designed to be a drag-and-drop wrapper, items should _always_ be resolvable
as filesystem paths. Therefore, it makes sense for a wrapper to actually
_test_ for this condition first, and discard any input that isn't a valid
path. I've added that to a new solution.
> Probably I have to use my solution using a temporary file. Possibly, but from what I can tell I think there will still be some issues.
The ideal technique is to pass the items in as single-quoted rather than
double-quoted strings. I've tried out another wrapper script, which is shown
below. This is actually a WSH script, and theoretically could be made more
generic, but for now it acts as a wrapper akin to the batch file concept.
Details:
(1) The script is totally generic, as long as you observe name and location
constraints. You may want to modify the -NoExit and possibly force the
PowerShell subscript to run hidden, but those are one-off modifications to
this script.
(2) As written, the WSH wrapper needs to be in the same location as the PS
script, with the same base name - e.g., if the PowerShell script is at
c:\apps\scripts\newdemo.ps1, then the WSH script needs to be
c:\apps\scripts\newdemo.vbs. You can of course put a shortcut to the WSH
script anywhere.
(3) The script is _explicitly_ designed for drag-and-drop. It tests every
single argument it was passed to see if it is an existing file or folder
path. If not, the item is dropped silently. Your comment about potential
code injection suggested this addition to me. Since we shouldn't get
anything _but_ file/folder paths as arguments, why not explicitly match the
argument data?
(4) The script works by explicitly single-quoting every argument, whether it
has embedded spaces or not. If an argument contains an embedded single-quote
(it's a legal filename character) then it is escaped by doubling. This
prevents use of any special tricks with filesystem names, but since it's
drag-and-drop, it won't matter.
(5) The only escaping done to the script name itself is escaping spaces in
the script name with a preceding backtick. This allows you to run PowerShell
scripts in situ from add-on installation folders that might be under Program
Files, or from per-user application folders.
' BEGIN WSH wrapper script
' Should have same basename as PS script it will run,
' and must be in same folder as PS script.
' ex: if c:\tmp\fred.ps1, this must be c:\tmp\fred.vbs
dim fso: Set fso = CreateObject("Scripting.FileSystemObject")
Dim WshScript: WshScript = WScript.ScriptFullName
Dim PsScript
PsScript = fso.BuildPath( _
fso.GetFile(WshScript).ParentFolder.Path, _
fso.GetBaseName(WshScript) & ".ps1")
' PowerShell scripters should probably know better than
' using a script name containing embedded spaces, but this
' may be unavoidable for some scripts.
' Just in case, we escape spaces with a backtick.
PsScript = Replace(PsScript, " ", "` ")
Dim i, arg
i = 0
Dim ArgSet: Set ArgSet = CreateObject("Scripting.Dictionary")
Argset(i) = PsScript
For each arg in WScript.Arguments
' EXPLICITLY ensure these resolve to file/folder paths
if fso.FileExists(arg) or fso.FolderExists(arg) then
i = i + 1
' Include escapes for single-quotes used in file/folder names
Argset(i) = "'" & Replace(arg, "'", "''") & "'"
End If
Next
' Remove -NoExit if you don't want PS to stay open when done.
Dim Command
Command = "powershell -NoLogo -NoExit -Command ""& {" _
& Join(ArgSet.Items) _
& "}"""
' WScript.Echo "command as passed to PowerShell:", Command
Dim WshShell: Set WshShell = CreateObject("WScript.Shell")
WshShell.Run Command
' Alternative operation (runs script hidden - ONLY use if
' you also remove -NoExit:
' WshShell.Run Command, 0
' END WSH Wrapper Script