While working on my previous question, I faced a similar issue that I was solving initially.
Actually, I spent this month in attempts to create one universal .bat helper library that will handle drag-and-dropped files with any names as best as it is possible. I want to do it transparently for the caller, and backward-compatible with any existing scripts, so filenames are delivered as %1, %2, and I have no problems with that.
Solution of @MCND is to read %CMDCMDLINE%
and then tricky parse it. So I can simply write like set "a=%CMDCMDLINE:"=?%"
– and have the full string into variable A, but all quotes inside are instantly replaced with question marks ?
, resulting in an unquoted line that I can easily process further.
But it works only for .bat drag-and-drop, since that way I can’t read parameters passed from command prompt. I have to use %*
.
My current question is: how to read it if the parameters may also contain quotation characters, even unpaired?
Consider the following BAT file: (let’s call "C:\test.bat")
@echo off
echo START
setlocal enabledelayedexpansion
set a=%1
echo a=[!a!]
set b="%1"
echo b=[!b!]
set "c=%1"
echo c=[!c!]
set d=%~1
echo d=[!d!]
set e="%~1"
echo e=[!e!]
set "f=%~1"
echo f=[!f!]
set g=%*
echo g=[!g!]
set h="%*"
echo h=[!h!]
set "i=%*"
echo i=[!i!]
endlocal
echo DONE
exit /b
Basically it is everything that I can imagine to read incoming batch ("call") percent-parameters: forms %1
, %~1
, %*
in unquoted set x=y
, quoted set "x=y"
and explicitly quoted set x="y"
variations. Delayed expansion is only to safely echo the value (here I don’t care about examination marks !
in parameter for simplicity).
My final aim is to create a script that will never throw an error: so every possible errors should appear either before my code gets execution (at CMD prompt itself), or after it finished (when the caller will try to access a file with a broken name).
So in this example, between START and DONE ideally there should be no errors, but they will appear at "set" instructions, before the corresponding "echo".
Attempt #1
Command: test
Output:
START
a=[]
b=[""]
c=[]
d=[]
e=[""]
f=[]
g=[]
h=[""]
i=[]
DONE
Good, not errors at zero arguments. Let’s get some:
Attempt #2
Command: test 123 "56"
Output:
START
a=[123]
b=["123"]
c=[123]
d=[123]
e=["123"]
f=[123]
g=[123 "56"]
h=["123 "56""]
i=[123 "56"]
DONE
Looks like for "normal" strings any method works without errors.
Attempt #3
Command: test ^&-
Output:
START
'-' is not recognized as an internal or external command,
operable program or batch file.
a=[]
b=["&-"]
c=[&-]
'-' is not recognized as an internal or external command,
operable program or batch file.
d=[]
e=["&-"]
f=[&-]
'-' is not recognized as an internal or external command,
operable program or batch file.
g=[]
h=["&-"]
i=[&-]
DONE
As you can see, cases A, D and G failed (=%1
, =%~1
and =%*
).
Attempt #4
Command: test "&-"
Output:
START
a=["&-"]
'-""' is not recognized as an internal or external command,
operable program or batch file.
b=[""]
'-""' is not recognized as an internal or external command,
operable program or batch file.
c=[]
'-""' is not recognized as an internal or external command,
operable program or batch file.
d=[]
e=["&-"]
f=[&-]
g=["&-"]
'-""' is not recognized as an internal or external command,
operable program or batch file.
h=[""]
'-""' is not recognized as an internal or external command,
operable program or batch file.
i=[]
DONE
Now cases B, C, D, H and I are failed (="%1"
, "=%1"
, =%~1
, ="%*"
and "=%*"
).
Attempt #5
Command: test ""^&-"
Output:
START
'-"' is not recognized as an internal or external command,
operable program or batch file.
a=[""]
b=["""&-""]
c=[""&-"]
d=["&-]
'-"' is not recognized as an internal or external command,
operable program or batch file.
e=[""]
'-"' is not recognized as an internal or external command,
operable program or batch file.
f=[]
'-"' is not recognized as an internal or external command,
operable program or batch file.
g=[""]
h=["""&-""]
i=[""&-"]
DONE
Failed the rest two cases E and F (="%~1"
and "=%~1"
) along with A and G (=%1
and %*
).
So, none of "set" variants will work reliably in all attempts to break my code!
Am I missing some other approach to read current parameters that come from untrusted source?
Can I get %…
in a normal variable %…%
? Can I replace quotes "
directly in %…
? Can I put %*
in some special context where things like ampersand &
may not cause change of execution? Is there another way to get arguments (calling CMD again, use temporary files – anything?)
I’m fine even if it will destroy the original string in these cases (unpaired quotes, unescaped specials, etc.), but I need to get rid of parse error (causing uncontrolled code execution)!