From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: taylanbayirli@gmail.com (Taylan Ulrich =?utf-8?Q?Bay=C4=B1rl=C4=B1?= =?utf-8?Q?=2FKammer?=) Newsgroups: gmane.emacs.devel Subject: Re: [PATCH] Add shell-quasiquote. Date: Sun, 18 Oct 2015 00:00:19 +0200 Message-ID: <87twpptato.fsf@T420.taylan> References: <87si59wj42.fsf@T420.taylan> <83eggt4esi.fsf@gnu.org> <87fv19wh7b.fsf@T420.taylan> <83bnbx4d7e.fsf@gnu.org> <87twppuzfu.fsf@T420.taylan> <83a8rh48if.fsf@gnu.org> <87io65utmt.fsf@T420.taylan> <5622B337.4050700@yandex.ru> <876125uqzw.fsf@T420.taylan> <5622BE84.8030209@yandex.ru> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: ger.gmane.org 1445119239 6667 80.91.229.3 (17 Oct 2015 22:00:39 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sat, 17 Oct 2015 22:00:39 +0000 (UTC) Cc: Eli Zaretskii , emacs-devel@gnu.org To: Dmitry Gutov Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sun Oct 18 00:00:30 2015 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1ZnZWf-00089k-Di for ged-emacs-devel@m.gmane.org; Sun, 18 Oct 2015 00:00:29 +0200 Original-Received: from localhost ([::1]:59900 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZnZWe-0003LU-Ib for ged-emacs-devel@m.gmane.org; Sat, 17 Oct 2015 18:00:28 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:52504) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZnZWa-0003LP-Fu for emacs-devel@gnu.org; Sat, 17 Oct 2015 18:00:25 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZnZWZ-0004NP-3S for emacs-devel@gnu.org; Sat, 17 Oct 2015 18:00:24 -0400 Original-Received: from mail-wi0-x22a.google.com ([2a00:1450:400c:c05::22a]:34082) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZnZWY-0004NL-Q5; Sat, 17 Oct 2015 18:00:23 -0400 Original-Received: by wikq8 with SMTP id q8so4861674wik.1; Sat, 17 Oct 2015 15:00:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:references:date:in-reply-to:message-id :user-agent:mime-version:content-type; bh=6FwnKRrnlT2pt88i3bhizm2N4BQD+EqE1A+NH2HOJUY=; b=L4RzqUhHGJDg5LiHmoBI+xPGRYjNxaAG/+ZWWtoVLoomX1k1Dvh0JIouAXjzY/HLkX FYsx4YZgE9YnMeClQ3kWOUqkuXR82saV26b1kjO8WQYiMH8ttP37BhIfswJkbwpG/6vp lCoMjms+Z+MUGPGk3QVvLhM84+s8FuPvWDY/izwFjZ5VNqbTbyoAxQ5qcrn3zwOyh0tP jmgm2D6v6xbrpebO5i5YRsOJW4HHpTndH4iNdag+iBrWAwuFb2NtWVe3bQ2OK0qslBu+ mTZkXU3mqmALjQwC1lybMnqrUrDt74Zk0t3W1QoUUPZ91H4cRT3wVibKAzoxNLvkU1IS Le6Q== X-Received: by 10.180.86.197 with SMTP id r5mr5313465wiz.36.1445119222007; Sat, 17 Oct 2015 15:00:22 -0700 (PDT) Original-Received: from T420.taylan ([2a02:908:c32:4740:221:ccff:fe66:68f0]) by smtp.gmail.com with ESMTPSA id gt4sm8592914wib.21.2015.10.17.15.00.20 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 17 Oct 2015 15:00:20 -0700 (PDT) In-Reply-To: <5622BE84.8030209@yandex.ru> (Dmitry Gutov's message of "Sun, 18 Oct 2015 00:32:52 +0300") User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux) X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2a00:1450:400c:c05::22a X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:191892 Archived-At: --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Dmitry Gutov writes: > On 10/18/2015 12:25 AM, Taylan Ulrich Bay=C4=B1rl=C4=B1/Kammer wrote: > >> Not knowing that there are bugs is not proof that there are no bugs. > > If you can't point out a bug, you have no justification to not use the > standard function. No, I will *not* let users of my code potentially suffer from arbitrary code injection attacks, thank you very much. >>> Either way, please avoid reinventing the wheel. >> >> It's not a reinvention because it has very strict semantics with regard >> to safety guarantees, which shell-quote-argument apparently doesn't. > > shell-quote-argument doesn't guarantee quoting the argument? Apparently, it doesn't. See Random832's demonstration of an injection attack on shell-quote-argument when it's used with csh. My function has a clearly defined domain, and operates correctly within that domain. Shell-quote-argument apparently does not even have a clearly defined domain in which it's supposed to work. If anyone has a shell argument quoting function that expands the domain of supported shells in a well-defined manner, without weakening the safety guarantees, please let me know. Until then, please accept this modified patch which clarifies that the library is not supposed to work with any shells other than ones conforming to POSIX sh. Taylan --=-=-= Content-Type: text/x-diff; charset=utf-8 Content-Disposition: inline; filename=0001-Add-shell-quasiquote.patch Content-Transfer-Encoding: quoted-printable >From f8bee6aeed124f0e14fbc55509597cddba439cbd Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?Taylan=3D20Ulrich=3D20Bay=3DC4=3DB1rl=3DC4=3DB1/Kammer?=3D Date: Sat, 17 Oct 2015 18:32:22 +0200 Subject: [PATCH] Add shell-quasiquote. --- packages/shell-quasiquote/shell-quasiquote.el | 152 ++++++++++++++++++++++= ++++ 1 file changed, 152 insertions(+) create mode 100644 packages/shell-quasiquote/shell-quasiquote.el diff --git a/packages/shell-quasiquote/shell-quasiquote.el b/packages/shell= -quasiquote/shell-quasiquote.el new file mode 100644 index 0000000..7f20afd --- /dev/null +++ b/packages/shell-quasiquote/shell-quasiquote.el @@ -0,0 +1,152 @@ +;;; shell-quasiquote.el --- Turn s-expressions into shell command strings. + +;; Copyright (C) 2015 Free Software Foundation, Inc. + +;; Author: Taylan Ulrich Bay=C4=B1rl=C4=B1/Kammer +;; Keywords: extensions, unix +;; URL: https://github.com/TaylanUB/emacs-shell-quasiquote + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; "Shell quasiquote" -- turn s-expressions into POSIX shell command strin= gs. +;; +;; Shells other than POSIX sh are not supported and WILL make your code su= bject +;; to arbitrary code injection attacks. Otherwise... +;; +;; Quoting is automatic and safe against injection. +;; +;; (let ((file1 "file one") +;; (file2 "file two")) +;; (shqq (cp -r ,file1 ,file2 "My Files"))) +;; =3D> "cp -r 'file one' 'file two' 'My Files'" +;; +;; You can splice many arguments into place with ,@foo. +;; +;; (let ((files (list "file one" "file two"))) +;; (shqq (cp -r ,@files "My Files"))) +;; =3D> "cp -r 'file one' 'file two' 'My Files'" +;; +;; Note that the quoting disables a variety of shell expansions like ~/foo, +;; $ENV_VAR, and e.g. {x..y} in GNU Bash. +;; +;; You can use ,,foo to escape the quoting. +;; +;; (let ((files "file1 file2")) +;; (shqq (cp -r ,,files "My Files"))) +;; =3D> "cp -r file1 file2 'My Files'" +;; +;; And ,,@foo to splice and escape quoting. +;; +;; (let* ((arglist '("-x 'foo bar' -y baz")) +;; (arglist (append arglist '("-z 'qux fux'")))) +;; (shqq (command ,,@arglist))) +;; =3D> "command -x 'foo bar' -y baz -z 'qux fux'" +;; +;; Neat, eh? + + +;;; Code: + +;;; Like `shell-quote-argument', but much simpler in implementation. +(defun shqq--quote-string (string) + (concat "'" (replace-regexp-in-string "'" "'\\\\''" string) "'")) + +(defun shqq--atom-to-string (atom) + (cond + ((symbolp atom) (symbol-name atom)) + ((stringp atom) atom) + ((numberp atom) (number-to-string atom)) + (t (error "Bad shqq atom: %S" atom)))) + +(defun shqq--quote-atom (atom) + (shqq--quote-string (shqq--atom-to-string atom))) + +(defun shqq--match-comma (form) + "Matches FORM against ,foo i.e. (\, foo) and returns foo. +Returns nil if FORM didn't match. You can't disambiguate between +FORM matching ,nil and not matching." + (if (and (consp form) + (eq '\, (car form)) + (consp (cdr form)) + (null (cddr form))) + (cadr form))) + +(defun shqq--match-comma2 (form) + "Matches FORM against ,,foo i.e. (\, (\, foo)) and returns foo. +Returns nil if FORM didn't match. You can't disambiguate between +FORM matching ,,nil and not matching." + (if (and (consp form) + (eq '\, (car form)) + (consp (cdr form)) + (null (cddr form))) + (shqq--match-comma (cadr form)))) + + +(defmacro shqq (parts) + "First, PARTS is turned into a list of strings. For this, +every element of PARTS must be one of: + +- a symbol, evaluating to its name, + +- a string, evaluating to itself, + +- a number, evaluating to its decimal representation, + +- \",expr\", where EXPR must evaluate to an atom that will be + interpreted according to the previous rules, + +- \",@list-expr\", where LIST-EXPR must evaluate to a list whose + elements will each be interpreted like the EXPR in an \",EXPR\" + form, and spliced into the list of strings, + +- \",,expr\", where EXPR is interpreted like in \",expr\", + +- or \",,@expr\", where EXPR is interpreted like in \",@expr\". + +In the resulting list of strings, all elements except the ones +resulting from \",,expr\" and \",,@expr\" forms are quoted for +shell grammar. + +Finally, the resulting list of strings is concatenated with +separating spaces." + (let ((parts + (mapcar + (lambda (part) + (cond + ((atom part) (shqq--quote-atom part)) + ;; We use the match-comma helpers because pcase can't match ,= foo. + (t (pcase part + ;; ,,foo i.e. (, (, foo)) + ((pred shqq--match-comma2) + (shqq--match-comma2 part)) + ;; ,,@foo i.e. (, (,@ foo)) + ((and (pred shqq--match-comma) + (let `,@,form (shqq--match-comma part))) + `(mapconcat #'identity ,form " ")) + ;; ,foo + ;; Insert redundant 'and x' to work around debbugs#18554. + ((and x (pred shqq--match-comma)) + `(shqq--quote-atom ,(shqq--match-comma part))) + ;; ,@foo + (`,@,form + `(mapconcat #'shqq--quote-atom ,form " ")) + (_ + (error "Bad shqq part: %S" part)))))) + parts))) + `(mapconcat #'identity (list ,@parts) " "))) + +(provide 'shell-quasiquote) +;;; shell-quasiquote.el ends here --=20 2.5.0 --=-=-=--