A summary of data about the Ruby ecosystem.

https://github.com/thoughtbot/terrapin

Run shell commands safely, even with user-supplied values
https://github.com/thoughtbot/terrapin

Keywords from Contributors

rubygems mvc activerecord activejob rspec thoughtbot fixtures factory-girl factory-bot factories

Last synced: about 22 hours ago
JSON representation

Repository metadata

Run shell commands safely, even with user-supplied values

README.md

Terrapin

Run shell commands safely, even with user-supplied values

API reference

Usage

The basic, normal stuff:

line = Terrapin::CommandLine.new("echo", "hello 'world'")
line.command # => "echo hello 'world'"
line.run # => "hello world\n"

Interpolated arguments:

line = Terrapin::CommandLine.new("convert", ":in -scale :resolution :out")
line.command(in: "omg.jpg",
             resolution: "32x32",
             out: "omg_thumb.jpg")
# => "convert 'omg.jpg' -scale '32x32' 'omg_thumb.jpg'"

It prevents attempts at being bad:

line = Terrapin::CommandLine.new("cat", ":file")
line.command(file: "haha`rm -rf /`.txt") # => "cat 'haha`rm -rf /`.txt'"

line = Terrapin::CommandLine.new("cat", ":file")
line.command(file: "ohyeah?'`rm -rf /`.ha!") # => "cat 'ohyeah?'\\''`rm -rf /`.ha!'"

NOTE: It only does that for arguments interpolated via run, NOT arguments
passed into new (see 'Security' below):

line = Terrapin::CommandLine.new("echo", "haha`whoami`")
line.command # => "echo haha`whoami`"
line.run # => "hahawebserver\n"

This is the right way:

line = Terrapin::CommandLine.new("echo", "haha:whoami")
line.command(whoami: "`whoami`") # => "echo haha'`whoami`'"
line.run(whoami: "`whoami`") # => "haha`whoami`\n"

You can ignore the result:

line = Terrapin::CommandLine.new("noisy", "--extra-verbose", swallow_stderr: true)
line.command # => "noisy --extra-verbose 2>/dev/null"

# ... and on Windows...
line.command # => "noisy --extra-verbose 2>NUL"

If your command errors, you get an exception:

line = Terrapin::CommandLine.new("git", "commit")
begin
  line.run
rescue Terrapin::ExitStatusError => e
  e.message # => "Command 'git commit' returned 1. Expected 0"
end

If your command might return something non-zero, and you expect that, it's cool:

line = Terrapin::CommandLine.new("/usr/bin/false", "", expected_outcodes: [0, 1])
begin
  line.run
rescue Terrapin::ExitStatusError => e
  # => You never get here!
end

You don't have the command? You get an exception:

line = Terrapin::CommandLine.new("lolwut")
begin
  line.run
rescue Terrapin::CommandNotFoundError => e
  e # => the command isn't in the $PATH for this process.
end

But don't fear, you can specify where to look for the command:

Terrapin::CommandLine.path = "/opt/bin"
line = Terrapin::CommandLine.new("lolwut")
line.command # => "lolwut", but it looks in /opt/bin for it.

You can even give it a bunch of places to look:

FileUtils.rm("/opt/bin/lolwut")
File.open('/usr/local/bin/lolwut') { |f| f.write('echo Hello') }
Terrapin::CommandLine.path = ["/opt/bin", "/usr/local/bin"]
line = Terrapin::CommandLine.new("lolwut")
line.run # => prints 'Hello', because it searches the path

Or just put it in the command:

line = Terrapin::CommandLine.new("/opt/bin/lolwut")
line.command # => "/opt/bin/lolwut"

You can see what's getting run. The 'Command' part it logs is in green for
visibility! (where applicable)

line = Terrapin::CommandLine.new("echo", ":var", logger: Logger.new(STDOUT))
line.run(var: "LOL!") # => Logs this with #info -> Command :: echo 'LOL!'

Or log every command:

Terrapin::CommandLine.logger = Logger.new(STDOUT)
Terrapin::CommandLine.new("date").run # => Logs this -> Command :: date

Security

Short version: Only pass user-generated data into the run method and NOT
new.

As shown in examples above, Terrapin will only shell-escape what is passed in as
interpolations to the run method. It WILL NOT escape what is passed in to the
second argument of new. Terrapin assumes that you will not be manually
passing user-generated data to that argument and will be using it as a template
for your command line's structure.

Runners

Terrapin will choose from among a couple different ways of running commands.
The simplest is Process.spawn, which is also the default. Terrapin can also just use backticks, so if for some reason you'd prefer that, you can ask Terrapin to use that:

Terrapin::CommandLine.runner = Terrapin::CommandLine::BackticksRunner.new

And if you really want to, you can define your own Runner, though I can't imagine why you would.

Terrapin::CommandLine.runner = Terrapin::CommandLine::BackticksRunner.new

And if you really want to, you can define your own Runner, though I can't
imagine why you would.

JRuby issues

Caveat

If you get Error::ECHILD errors and are using JRuby, there is a very good
chance that the error is actually in JRuby. This was brought to our attention
in https://github.com/thoughtbot/terrapin/issues/24 and probably fixed in
http://jira.codehaus.org/browse/JRUBY-6162. You will want to use the
BackticksRunner if you are unable to update JRuby.

Spawn warning

If you get unsupported spawn option: out warning (like in issue
38
), try to use
PopenRunner:

Terrapin::CommandLine.runner = Terrapin::CommandLine::PopenRunner.new

Thread Safety

Terrapin should be thread safe. As discussed here, in this climate_control
thread
, climate_control,
which modifies the environment under which commands are run for the
BackticksRunner and PopenRunner, is thread-safe but not reentrant. Please let us
know if you find this is ever not the case.

Feedback

Security concerns must be privately emailed to
security@thoughtbot.com.

Question? Idea? Problem? Bug? Comment? Concern? Like using question marks?

GitHub Issues For All!

Credits

Thank you to all the
contributors
!

License

Copyright © 2011 Jon Yurek and thoughtbot, inc. This is free software, and
may be redistributed under the terms specified in the
LICENSE
file.

About thoughtbot

thoughtbot

This repo is maintained and funded by thoughtbot, inc.
The names and logos for thoughtbot are trademarks of thoughtbot, inc.

We love open source software!
See our other projects.
We are available for hire.


Owner metadata


GitHub Events

Total
Last Year

Committers metadata

Last synced: 3 days ago

Total Commits: 202
Total Committers: 47
Avg Commits per committer: 4.298
Development Distribution Score (DDS): 0.559

Commits in past year: 12
Committers in past year: 4
Avg Commits per committer in past year: 3.0
Development Distribution Score (DDS) in past year: 0.333

Name Email Commits
Jon Yurek j****k@t****m 89
Mike Burns m****e@m****m 17
Gabe Berke-Williams g****e@t****m 10
Nick Charlton n****k@n****t 8
Adarsh Pandit a****h@t****m 6
github-actions[bot] g****] 6
Elisa Verna e****a@g****m 5
Gabe Berke-Williams g****w@g****m 4
Jonas S j****s@p****e 4
sapslaj s****j@g****m 4
Josh Clayton j****n@g****m 3
Ken Dreyer k****r@k****m 3
Prem Sichanugrist s@s****u 2
Jeremy Neander s****o@g****m 2
James Dean Shepherd j****7@g****m 2
Guillermo Pascual a****e@g****m 2
Aleksei Gusev a****v@g****m 2
Brian Durand b****n@e****m 2
Sara Jackson 1****6 2
Ivo van Hurne i****e@b****l 2
jon Yurek and Joel Quenneville j****q@t****m 1
Stefanni Brasil s****l@g****m 1
Cédric FABIANSKI c****i@l****m 1
Alban Peignier a****n@t****u 1
sshaw s****w@g****m 1
cthulhu666 j****i@g****m 1
Tanner Smith t****r@t****t 1
Stefanni Brasil s****l@p****e 1
Spencer Steffen s****r@c****m 1
Radosław Bułat r****t@g****m 1
and 17 more...

Committer domains:


Issue and Pull Request metadata

Last synced: 11 days ago

Total issues: 13
Total pull requests: 56
Average time to close issues: 12 months
Average time to close pull requests: 4 months
Total issue authors: 13
Total pull request authors: 21
Average comments per issue: 1.23
Average comments per pull request: 0.95
Merged pull request: 41
Bot issues: 0
Bot pull requests: 13

Past year issues: 1
Past year pull requests: 20
Past year average time to close issues: N/A
Past year average time to close pull requests: 14 days
Past year issue authors: 1
Past year pull request authors: 5
Past year average comments per issue: 0.0
Past year average comments per pull request: 0.5
Past year merged pull request: 14
Past year bot issues: 0
Past year bot pull requests: 2

More stats: https://issues.ecosyste.ms/repositories/lookup?url=https://github.com/thoughtbot/terrapin

Top Issue Authors

  • jayjlawrence (1)
  • briankung (1)
  • thomasdarde (1)
  • masudhossain (1)
  • RevathiNarjala (1)
  • cdesch (1)
  • lorennorman (1)
  • jjb (1)
  • hector (1)
  • gustavobap (1)
  • robstove (1)
  • edward-ellis (1)
  • RebelLion420 (1)

Top Pull Request Authors

  • nickcharlton (13)
  • github-actions[bot] (13)
  • stefannibrasil (4)
  • biow0lf (3)
  • rocket-turtle (3)
  • neilvcarvalho (2)
  • leifg (2)
  • franmomu (2)
  • jamesds (2)
  • jyurek (1)
  • epistrephein (1)
  • mjankowski (1)
  • brimatteng (1)
  • mike-burns (1)
  • lostapathy (1)

Top Issue Labels

Top Pull Request Labels


Package metadata

gem.coop: terrapin

Run shell commands safely, even with user-supplied values

  • Homepage: https://github.com/thoughtbot/terrapin
  • Documentation: http://www.rubydoc.info/gems/terrapin/
  • Licenses: MIT
  • Latest release: 1.1.1 (published 8 months ago)
  • Last Synced: 2026-03-02T03:02:30.647Z (1 day ago)
  • Versions: 6
  • Dependent Packages: 0
  • Dependent Repositories: 0
  • Downloads: 69,955,664 Total
  • Docker Downloads: 17,285,690
  • Rankings:
    • Dependent repos count: 0.0%
    • Dependent packages count: 0.0%
    • Average: 0.358%
    • Downloads: 0.413%
    • Docker downloads count: 1.021%
  • Maintainers (3)
rubygems.org: terrapin

Run shell commands safely, even with user-supplied values

  • Homepage: https://github.com/thoughtbot/terrapin
  • Documentation: http://www.rubydoc.info/gems/terrapin/
  • Licenses: MIT
  • Latest release: 1.1.1 (published 8 months ago)
  • Last Synced: 2026-02-28T20:00:48.982Z (3 days ago)
  • Versions: 6
  • Dependent Packages: 30
  • Dependent Repositories: 4,741
  • Downloads: 69,939,001 Total
  • Docker Downloads: 17,285,690
  • Rankings:
    • Downloads: 0.398%
    • Dependent repos count: 0.458%
    • Dependent packages count: 0.803%
    • Docker downloads count: 1.111%
    • Average: 2.093%
    • Stargazers count: 3.724%
    • Forks count: 6.061%
  • Maintainers (3)
proxy.golang.org: github.com/thoughtbot/terrapin

  • Homepage:
  • Documentation: https://pkg.go.dev/github.com/thoughtbot/terrapin#section-documentation
  • Licenses: other
  • Latest release: v1.1.1 (published 8 months ago)
  • Last Synced: 2026-02-28T20:00:50.153Z (3 days ago)
  • Versions: 23
  • Dependent Packages: 0
  • Dependent Repositories: 0
  • Rankings:
    • Stargazers count: 3.508%
    • Forks count: 4.996%
    • Average: 7.212%
    • Dependent packages count: 9.564%
    • Dependent repos count: 10.779%
ubuntu-20.04: ruby-terrapin

  • Homepage: https://github.com/thoughtbot/terrapin
  • Licenses:
  • Latest release: 0.6.0-2 (published 18 days ago)
  • Last Synced: 2026-02-13T07:24:00.181Z (18 days ago)
  • Versions: 1
  • Dependent Packages: 0
  • Dependent Repositories: 0
  • Rankings:
    • Dependent repos count: 0.0%
    • Dependent packages count: 0.0%
    • Average: 100%
debian-11: ruby-terrapin

  • Homepage: https://github.com/thoughtbot/terrapin
  • Documentation: https://packages.debian.org/bullseye/ruby-terrapin
  • Licenses:
  • Latest release: 0.6.0-2 (published 21 days ago)
  • Last Synced: 2026-02-13T08:25:36.846Z (18 days ago)
  • Versions: 1
  • Dependent Packages: 0
  • Dependent Repositories: 0
  • Rankings:
    • Dependent repos count: 0.0%
    • Dependent packages count: 0.0%
    • Average: 100%
ubuntu-22.04: ruby-terrapin

  • Homepage: https://github.com/thoughtbot/terrapin
  • Licenses:
  • Latest release: 0.6.0-4 (published 18 days ago)
  • Last Synced: 2026-02-13T13:27:21.286Z (18 days ago)
  • Versions: 1
  • Dependent Packages: 0
  • Dependent Repositories: 0
  • Rankings:
    • Dependent repos count: 0.0%
    • Dependent packages count: 0.0%
    • Average: 100%

Dependencies

Gemfile rubygems
  • posix-spawn >= 0
terrapin.gemspec rubygems
  • activesupport >= 3.0.0, < 5.0 development
  • bourne >= 0 development
  • mocha >= 0 development
  • pry >= 0 development
  • rake >= 0 development
  • rspec >= 0 development
  • climate_control >= 0.0.3, < 1.0
.github/workflows/ci.yml actions
  • actions/checkout v2 composite
  • ruby/setup-ruby v1 composite

Score: 28.483408056416565