Shouldn't this type of utility rather be a library if it's to be used from other applications such as web apps?
As for the wisdom of this approach...
You could feasibly do the same stuff in an interpreted language, but (I suspect) it would be substantially slower than directly running those compiled command-line tools underlying it.
That sounds bass ackwards if you ask me. A command line tool is usually a thin wrapper around a library, for a reason.
I realize it might seem crazy to duplicate functionality from gs into your library, but then the tool should link against those code bases as libraries instead of calling some gs executable (which of course in turn is just a wrapper around a gs library and so on).
Making a library call to re-scale an image or convert from formatA to formatB shouldn't be a shell execute call to begin with, and it shouldn't be making any shell exec calls of its own.
It's historically grown. The tool came first, the library was tacked on later.
And yes, it's a mess. That's why the much saner GraphicsMagick was forked off it⦠in 2002. People just never migrated.
That being said, I wouldn't be surprised if porting those things over from compiled languages to be a part of the library would prove to be substantially slower. For high-volume applications, it would very possibly make the library totally unusable.
I agree that it's a backwards way of doing things in a perfect world, but the perfect is the enemy of the good. Sometimes you've gotta make compromises for reality's sake.
I must have missed something, I thought ImageMagick was a collection of C libraries and C programs, which in turn occasionally called other libraries and programs (such as gs).
What I meant was that a graphics utility should be a c library that exports functions like
scale_image(source_image, target_image, scale)
to be used instead of running a command line utility convert image.jpg -resize 0.5
That can't ever be slower, but some times likely faster (e.g. to recode an image before inserting as a blob into a db or sending over a netwoek you wouldn't even write it to disk).I have never heard of a program where the command line program is at the heart, and a library is wrapped around it. I can't even imagine how such code would be structured.
http://bonedaddy.net/pabs3/log/2014/02/17/pid-preservation-s... http://libpipeline.nongnu.org/
I expect there may also be option injection vulnerabilities in the code.
http://www.defensecode.com/public/DefenseCode_Unix_WildCards...
Edit: Actually it already does: https://github.com/ImageMagick/ImageMagick/blob/master/Magic...
* https://msdn.microsoft.com/en-gb/library/windows/desktop/ms6...
A single command tail is the Win32 model at base, as it was the DOS API model before it. (The OS/2 model was multiple command tails, although in practice most programs read no more than just one.) exec*() implementations are layered on top of this, and the argv[] notion is a shared fiction maintained by the runtime libraries of C and C++ implementations.
This one [1] seems to deal with ImageMagick's feature that reads the contents of a file into the command line: "The special character '@' at the start of a filename, means replace the filename, with contents of the given file. That is you can read a file containing a list of files!" Again though, that's a filename-based problem, not something you'd use magic number checking to defend against.
Edit: I guess with the "@filename"-based one, you could defend on the basis that the payload will be a text file of filenames, but that seems rickety at best.
1: https://github.com/ImageMagick/ImageMagick/commit/58a2ce1638...
In particular, ImageMagick accepting MSL directly into convert seems like an extremely straightforward exploit path, so much so that it actually seems unlikely. Their documentation makes it seem like it's designed to use a separate command "conjure," but... some combination of factors is at play here, anyway.
s/'/'\''/g
s/^/'/
s/$/'/
> Enclosing characters in single quotes preserves the literal meaning of all the characters (except single quotes, making it impossible to put single-quotes in a single-quoted string).
Note also the following examples, with double-quoted strings:
$ echo a b c
a b c
$ echo "a" "b" "c"
a b c
$ echo "a""b""c"
abc
$ echo "a" b "c"
a b c
$ echo "a"b"c"
abc
The same concatenation rules apply to single-quoted strings. So, by putting single quotes at the beginning and end of a string, you only need to worry about single quotes within the string. You can "escape" those with '\'', where the first single quote terminates the preceding single-quoted string, the backslash+single-quote pair is a literal unquoted/escaped single quote in the shell, and the final single quote begins single-quoting again for the rest of the string. The three parts are then dequoted and concatenated together back into your original string by the shell. $ echo 'hi\'
hi\In the meantime, maybe a DONT_RUN_COMMANDS ifdef around every call to fork/system/exec is merited...
If you are looking for a small, standalone, non-copyleft alternative, the closest I can think of is the [exec] command in the Jim Tcl [1] embedded scripting language.
Here is an example (without the error checks you'd have in production code):
#include "jim.h"
int main(int argc, char const *argv[])
{
Jim_Interp *interp;
int error;
Jim_Obj *cmd;
interp = Jim_CreateInterp();
Jim_RegisterCoreCommands(interp);
Jim_InitStaticExtensions(interp);
// The input redirect below does *not* invoke the POSIX shell. It is handled by Jim Tcl itself.
cmd = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, cmd, Jim_NewStringObj(interp, "exec", -1));
Jim_ListAppendElement(interp, cmd, Jim_NewStringObj(interp, "awk", -1));
Jim_ListAppendElement(interp, cmd, Jim_NewStringObj(interp, "1", -1));
Jim_ListAppendElement(interp, cmd, Jim_NewStringObj(interp, "<", -1));
Jim_ListAppendElement(interp, cmd, Jim_NewStringObj(interp, "/etc/passwd", -1));
error = Jim_EvalObj(interp, cmd);
if (error != JIM_ERR) {
printf("%s\n", Jim_String(Jim_GetResult(interp)));
}
Jim_FreeInterp(interp);
return error;
}
While I am a fan of the language and of the "hard and soft layers" approach in general [2], it is a commitment: it requires you to embed the language runtime in your program and learn the basics of the scripting language itself and its C API. The upside is that Jim Tcl's [exec] works on Windows, too (and you get other goodies with Jim like a fast, high quality implementation of strings and hash maps).Alternatively, you can use the "big" Tcl [3] as a C library. It's larger but more mature and is available in every major Linux distribution.
[1] http://jim.tcl.tk/fossil/doc/trunk/Tcl_shipped.html#_exec
Either way you get a very fine C library that prevents you from succumbing to Greenspun's tenth rule, so I can only recommend it for most C programs and libraries of sufficient size and complexity.
I agree with you on writing in another language but presumably you wouldn't be considering libpipeline anyway unless you had to write C. My default approach when I need C to access some API is to write an extension to an interpreter from which to access it. I leave embedding an interpreter in C for when you can't do that.
If so then it's not complicated file formats or buffer overflows, it's an improperly escaped 'system' call being fed user input in an obscure feature that probably shouldn't have been included in the first place. Party like it's 1999 guys.
Edit: I'm pretty sure this is an RCE issue. This function[4] replaces the placeholders in the wget command, which is this: `wget -q -O "%o" "https:%M"`
So seeing as %M is user controlled we can feed it a URL like "//hacker.com/`rm -rf /`" and it will blindly pass it to the shell. Wow.
1. https://github.com/ImageMagick/ImageMagick/commit/a347456a1e...
2. https://github.com/ImageMagick/ImageMagick/blob/e93e339c0a44...
3. https://github.com/ImageMagick/ImageMagick/blob/e93e339c0a44...
4. https://github.com/ImageMagick/ImageMagick/blob/32bdefdc31f1...