Skip to content

Commit

Permalink
[Fix #2656] Base64 encode command when using PowerShell
Browse files Browse the repository at this point in the history
To avoid multiple layers of escaping when using tools.deps with
PowerShell, base64 encode the clojure command and arguments when
calling jack-in with `cider-clojure-cli-command` as `"powershell"`.

If no clojure command is found on a Windows system use "powershell"
for `cider-clojure-cli-command` by default.
  • Loading branch information
scottdw authored and bbatsov committed Aug 13, 2019
1 parent 31f83df commit cd68e20
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### New features

* [#2656](https://github.com/clojure-emacs/cider/issues/2656): Base64 encode clojure command and arguments on jack-in when `cider-clojure-cli-command` is `"powershell"` to avoid escaping issues. If no `clojure` command is found on Windows `cider-clojure-cli-command` defaults to `"powershell"`.
* Allow editing of jack in command with prefix or when `cider-edit-jack-in-command` is truthy.
* New defcustom `cider-repl-require-ns-on-set`: Set it to make cider require the namespace before setting it, when calling `cider-repl-set-ns`.
* [#2611](https://github.com/clojure-emacs/cider/issues/2611): Add `eval`-based classpath lookup fallback. It's used when cider-nrepl is not present.
Expand Down
21 changes: 17 additions & 4 deletions cider.el
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,14 @@ version from the CIDER package or library.")
:package-version '(cider . "0.9.0"))

(defcustom cider-clojure-cli-command
"clojure"
(if (and (eq system-type 'windows-nt)
(null (executable-find "clojure")))
"powershell"
"clojure")
"The command used to execute clojure with tools.deps (requires Clojure 1.9+).
Don't use clj here, as it doesn't work when spawned from Emacs due to
it using rlwrap."
Don't use clj here, as it doesn't work when spawned from Emacs due to it
using rlwrap. If on Windows and no \"clojure\" executable is found we
default to \"powershell\"."
:type 'string
:group 'cider
:safe #'stringp
Expand Down Expand Up @@ -1184,6 +1188,13 @@ non-nil, don't start if ClojureScript requirements are not met."
:safe #'booleanp
:version '(cider . "0.22.0"))

(defun cider--powershell-encode-command (cmd-params)
"Base64 encode the powershell command and jack-in CMD-PARAMS for clojure-cli."
(let* ((quoted-params (replace-regexp-in-string "\"" "\"\"" cmd-params))
(command (format "clojure %s" quoted-params))
(utf-16le-command (encode-coding-string command 'utf-16le)))
(format "-encodedCommand %s" (base64-encode-string utf-16le-command t))))

(defun cider--update-jack-in-cmd (params)
"Update :jack-in-cmd key in PARAMS."
(let* ((params (cider--update-do-prompt params))
Expand All @@ -1209,7 +1220,9 @@ non-nil, don't start if ClojureScript requirements are not met."
(and (null project-dir)
(eq cider-allow-jack-in-without-project 'warn)
(y-or-n-p "Are you sure you want to run `cider-jack-in' without a Clojure project? ")))
(let ((cmd (format "%s %s" command-resolved cmd-params)))
(let ((cmd (format "%s %s" command-resolved (if (string-equal command "powershell")
(cider--powershell-encode-command cmd-params)
cmd-params))))
(plist-put params :jack-in-cmd (if (or cider-edit-jack-in-command
(plist-get params :edit-jack-in-command))
(read-string "jack-in command: " cmd t)
Expand Down
6 changes: 6 additions & 0 deletions doc/modules/ROOT/pages/basics/up_and_running.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ these are passed to the command directly, in first position
usually task names and their parameters (e.g., `dev` for launching
boot's dev task instead of the standard `repl -s wait`).

To use `cider-jack-in` with `tools.deps` on Windows set the
`cider-clojure-cli-command` to `"powershell"`. This happens by default
if you are on Windows and no `clojure` executable is found. Using
`"powershell"` will Base64 encode the clojure launch command before
passing it to PowerShell and avoids shell-escaping issues.

== Connect to a Running nREPL Server

If you have an nREPL server already running, CIDER can connect to
Expand Down
28 changes: 28 additions & 0 deletions test/cider-tests.el
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,34 @@
(expect (cider--shadow-parse-builds (parseedn-read-str "[oops]"))
:to-equal '(browser-repl node-repl))))

(describe "cider--powershell-encode-command"
(it "base64 encodes command and parameters"
(expect (cider--powershell-encode-command "cmd-params")
:to-equal (concat "-encodedCommand "
;; Eval to reproduce reference string below: (base64-encode-string (encode-coding-string "clojure cmd-params" 'utf-16le) t)
"YwBsAG8AagB1AHIAZQAgAGMAbQBkAC0AcABhAHIAYQBtAHMA")))
(it "escapes double quotes by repeating them"
(expect (cider--powershell-encode-command "\"cmd-params\"")
:to-equal (concat "-encodedCommand "
;; Eval to reproduce reference string below: (base64-encode-string (encode-coding-string "clojure \"\"cmd-params\"\"" 'utf-16le) t)
"YwBsAG8AagB1AHIAZQAgACIAIgBjAG0AZAAtAHAAYQByAGEAbQBzACIAIgA="))))

(describe "cider--update-jack-in-cmd"
(describe "when 'clojure-cli project type and \"powershell\" command"
(it "returns a jack-in command using encodedCommand option"
(setq-local cider-clojure-cli-command "powershell")
(setq-local cider-inject-dependencies-at-jack-in nil)
(setq-local cider-allow-jack-in-without-project t)
(setq-local cider-edit-jack-in-command nil)
(spy-on 'cider-project-type :and-return-value 'clojure-cli)
(spy-on 'cider-jack-in-resolve-command :and-return-value "resolved-powershell")
(spy-on 'cider-jack-in-global-options)
(spy-on 'cider-jack-in-params :and-return-value "\"cmd-params\"")
(expect (plist-get (cider--update-jack-in-cmd nil) :jack-in-cmd)
:to-equal (concat "resolved-powershell -encodedCommand "
;; Eval to reproduce reference string below: (base64-encode-string (encode-coding-string "clojure \"\"cmd-params\"\"" 'utf-16le) t)
"YwBsAG8AagB1AHIAZQAgACIAIgBjAG0AZAAtAHAAYQByAGEAbQBzACIAIgA=")))))

(defmacro with-temp-shadow-config (contents &rest body)
"Run BODY with a mocked shadow-cljs.edn project file with the CONTENTS."
`(let* ((edn-file "shadow-cljs.edn")
Expand Down

0 comments on commit cd68e20

Please sign in to comment.