shellwords: Parse strings into words, like a shell would

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

Warnings:

See https://github.com/pbrisbin/hs-shellwords#readme


[Skip to Readme]

Properties

Versions 0.1.0.0, 0.1.1.0, 0.1.2.0, 0.1.2.1, 0.1.2.2, 0.1.3.0, 0.1.3.1, 0.1.3.2, 0.1.4.0, 0.1.4.1, 0.1.4.1, 0.1.4.2
Change log CHANGELOG.md
Dependencies base (>=4.18.2.1 && <5), megaparsec (>=9.5.0), text (>=2.0.2) [details]
License MIT
Copyright 2018 Patrick Brisbin
Author Patrick Brisbin
Maintainer pbrisbin@gmail.com
Category Text
Home page https://github.com/pbrisbin/hs-shellwords#readme
Bug tracker https://github.com/pbrisbin/hs-shellwords/issues
Source repo head: git clone https://github.com/pbrisbin/hs-shellwords
Uploaded by PatrickBrisbin at 2025-02-03T18:41:06Z

Modules

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Readme for shellwords-0.1.4.1

[back to package description]

ShellWords

Hackage Stackage Nightly Stackage LTS CI

Parse a string into words, like a shell would.

Motivation

If you want to execute a specific command with input given to you from an untrusted source, you should not give that text as-is to a shell:

let userInput = "push origin main"

callCommand $ "git " <> userInput
-- Forward output of the push command...

You may be tempted to do this because you want to correctly handle quoting and other notoriously-difficult word-splitting problems. But doing so is a severe security vulnerability:

let userInput = "push origin main; cat /etc/passwd"

callCommand $ "git " <> userInput
-- Forward output of the push command...
-- And then dump /etc/passwd. Oops.

Furthermore, any attempts to sanitize the string are unlikely to be 100% affective and should be avoided. The only safe way to do this is to not use a shell intermediary, and always exec a process directly:

let userInput = "push origin main"

callProcess "git" $ words userInput
-- Forward output of the push command...

Now, there's no vulnerability:

let userInput = "push origin main; cat /etc/passwd"

callProcess "git" $ words userInput
-- Invalid usage. :)

The new problem (but not a security-related one!) is how to correctly parse a string like "push origin main" into command arguments. The rules are complex enough that you probably want to get a library to do it.

So here we are.

Example

Right args <- parse "some -complex --command=\"Line And\" 'More'"

callProcess cmd args
--
-- Is equivalent to:
--
-- > callProcess cmd ["some", "-complex", "--command=Line And", "More"]
--

Unsafe Usage

The following is a perfectly reasonable thing one might do with this library:

Right (cmd:args) <- parse userInput

callProcess cmd args

However, if:

  1. userInput is un-trusted, and
  2. You do no further validation of what cmd can be,

Then this re-introduces the original security vulnerability and, at that point, you might as well just pass userInput to a shell.

Lineage

This package is inspired by and named after


CHANGELOG | LICENSE