From 56c969110143652410c48785118ab2a2c1d3519e Mon Sep 17 00:00:00 2001 From: Florrie Date: Thu, 9 Nov 2017 00:21:45 -0400 Subject: Let keybindings run shell commands, if --trust option is given Keybinding action format: [[..combo..], 'runShellCommand', ..command name.., [..arguments..]] (--trust is an alias for --trust-shell-commands. When --trust appears in an "options" property of a playlist (or anywhere besides the `http-music play` invocation on the command line, shell command permisisons are *revoked* - they cannot be enabled, even by directly passing --trust to the command line.) Also adds a note to todo.txt. --- src/loop-play.js | 1 - src/play.js | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/loop-play.js b/src/loop-play.js index 09b3cad..34ac4d5 100644 --- a/src/loop-play.js +++ b/src/loop-play.js @@ -11,7 +11,6 @@ const { spawn } = require('child_process') const FIFO = require('fifo-js') const EventEmitter = require('events') -const promisifyProcess = require('./promisify-process') const killProcess = require('./kill-process') const { HistoryController, generalPicker } = require('./pickers') diff --git a/src/play.js b/src/play.js index ac6a232..db2462c 100755 --- a/src/play.js +++ b/src/play.js @@ -3,12 +3,14 @@ 'use strict' const { promisify } = require('util') +const { spawn } = require('child_process') const clone = require('clone') const fs = require('fs') const fetch = require('node-fetch') const commandExists = require('./command-exists') const startLoopPlay = require('./loop-play') const processArgv = require('./process-argv') +const promisifyProcess = require('./promisify-process') const { compileKeybindings } = require('./keybinder') const processSmartPlaylist = require('./smart-playlist') @@ -88,6 +90,15 @@ async function main(args) { let disablePlaybackStatus = false + // Trust shell commands - permits keybindings to activate console commands. + let trustShellCommands = false + + // Whether or not "trust shell commands" *may* be set to true. Set to false + // when shell command permissions are revoked (to prevent them from being + // granted in the future). Basic protection against dumb attempts at Evil + // keybinding files. + let mayTrustShellCommands = true + const keybindings = [ [['space'], 'togglePause'], [['left'], 'seek', -5], @@ -536,7 +547,31 @@ async function main(args) { disablePlaybackStatus = true }, - '-hide-playback-status': util => util.alias('-disable-playback-status') + '-hide-playback-status': util => util.alias('-disable-playback-status'), + + '-trust-shell-commands': function(util) { + // --trust-shell-commands (alias: --trust) + // Lets keybindings run shell commands. Only use this when loading + // keybindings from a trusted source. Defaults to false (no shell + // permissions). + + // We don't want an imported playlist to enable this! - Only arguments + // directly passed to http-music from the command line. + if (util.argv !== args) { + console.warn( + "--trust-shell-commands must be passed directly to http-music " + + "from the command line! (Revoking shell command permissions.)" + ) + + trustShellCommands = false + mayTrustShellCommands = false + } else { + console.log("Trusting shell commands.") + trustShellCommands = true + } + }, + + '-trust': util => util.alias('-trust-shell-commands') } await openPlaylist('./playlist.json', true) @@ -660,6 +695,22 @@ async function main(args) { 'showTrackInfo': function() { clearConsoleLine() playController.logTrackInfo() + }, + + 'runShellCommand': async function(command, args) { + if (trustShellCommands) { + console.log( + 'From keybinding, running shell command:', + `${command} ${args.join(' ')}` + ) + await promisifyProcess(spawn(command, args)) + } else { + console.warn( + 'From keybinding, shell command requested but not executed', + '(no --trust):', + `${command} ${args.join(' ')}` + ) + } } } -- cgit 1.3.0-6-gf8a5