Skip to content

Commit

Permalink
refactor ZirconCode's Agent to only run when ENABLE_INSECURE_AGENTS i…
Browse files Browse the repository at this point in the history
…s enabled in .env
  • Loading branch information
cantino committed Apr 19, 2014
1 parent 317642a commit a67231f
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 171 deletions.
4 changes: 4 additions & 0 deletions .env.example
Expand Up @@ -86,6 +86,10 @@ AWS_SANDBOX=false
# You should not allow this on a shared Huginn box because it is not secure.
ALLOW_JSONPATH_EVAL=false

# Enable this setting to allow insecure Agents like the ShellCommandAgent. Only do this
# when you trust everyone using your Huginn installation.
ENABLE_INSECURE_AGENTS=false

# Use Graphviz for generating diagrams instead of using Google Chart
# Tools. Specify a dot(1) command path built with SVG support
# enabled.
Expand Down
101 changes: 0 additions & 101 deletions app/models/agents/command_agent.rb

This file was deleted.

111 changes: 111 additions & 0 deletions app/models/agents/shell_command_agent.rb
@@ -0,0 +1,111 @@
require 'open3'

module Agents
class ShellCommandAgent < Agent
default_schedule "never"

def self.should_run?
ENV['ENABLE_INSECURE_AGENTS'] == "true"
end

description <<-MD
The ShellCommandAgent can execute commands on your local system, returning the output.
`command` specifies the command to be executed, and `path` will tell ShellCommandAgent in what directory to run this command.
`expected_update_period_in_days` is used to determine if the Agent is working.
ShellCommandAgent can also act upon received events. These events may contain their own `path` and `command` values. If they do not, ShellCommandAgent will use the configured options. For this reason, please specify defaults even if you are planning to have this Agent to respond to events.
The resulting event will contain the `command` which was executed, the `path` it was executed under, the `exit_status` of the command, the `errors`, and the actual `output`. ShellCommandAgent will not log an error if the result implies that something went wrong.
*Warning*: This type of Agent runs arbitrary commands on your system, #{Agents::ShellCommandAgent.should_run? ? "but is **currently enabled**" : "and is **currently disabled**"}.
Only enable this Agent if you trust everyone using your Huginn installation.
You can enable this Agent in your .env file by setting `ENABLE_INSECURE_AGENTS` to `true`.
MD

event_description <<-MD
Events look like this:
{
'command' => 'pwd',
'path' => '/home/Huginn',
'exit_status' => '0',
'errors' => '',
'output' => '/home/Huginn'
}
MD

def default_options
{
'path' => "/",
'command' => "pwd",
'expected_update_period_in_days' => 1
}
end

def validate_options
unless options['path'].present? && options['command'].present? && options['expected_update_period_in_days'].present?
errors.add(:base, "The path, command, and expected_update_period_in_days fields are all required.")
end

unless File.directory?(options['path'])
errors.add(:base, "#{options['path']} is not a real directory.")
end
end

def working?
Agents::ShellCommandAgent.should_run? && event_created_within?(options['expected_update_period_in_days']) && !recent_error_logs?
end

def receive(incoming_events)
incoming_events.each do |event|
handle(event.payload, event)
end
end

def check
handle(options)
end

private

def handle(opts = options, event = nil)
if Agents::ShellCommandAgent.should_run?
command = opts['command'] || options['command']
path = opts['path'] || options['path']

result, errors, exit_status = run_command(path, command)

vals = {"command" => command, "path" => path, "exit_status" => exit_status, "errors" => errors, "output" => result}
created_event = create_event :payload => vals

log("Ran '#{command}' under '#{path}'", :outbound_event => created_event, :inbound_event => event)
else
log("Unable to run because insecure agents are not enabled. Edit ENABLE_INSECURE_AGENTS in the Huginn .env configuration.")
end
end

def run_command(path, command)
result = nil
errors = nil
exit_status = nil

Dir.chdir(path){
begin
stdin, stdout, stderr, wait_thr = Open3.popen3(command)
exit_status = wait_thr.value.to_i
result = stdout.gets(nil)
errors = stderr.gets(nil)
rescue Exception => e
errors = e.to_s
end
}

result = result.to_s.strip
errors = errors.to_s.strip

[result, errors, exit_status]
end
end
end
70 changes: 0 additions & 70 deletions spec/models/agents/command_agent_spec.rb

This file was deleted.

99 changes: 99 additions & 0 deletions spec/models/agents/shell_command_agent_spec.rb
@@ -0,0 +1,99 @@
require 'spec_helper'

describe Agents::ShellCommandAgent do
before do
@valid_path = Dir.pwd

@valid_params = {
:path => @valid_path,
:command => "pwd",
:expected_update_period_in_days => "1",
}

@checker = Agents::ShellCommandAgent.new(:name => "somename", :options => @valid_params)
@checker.user = users(:jane)
@checker.save!

@event = Event.new
@event.agent = agents(:jane_weather_agent)
@event.payload = {
:command => "ls"
}
@event.save!

stub(Agents::ShellCommandAgent).should_run? { true }
end

describe "validation" do
before do
@checker.should be_valid
end

it "should validate presence of necessary fields" do
@checker.options[:command] = nil
@checker.should_not be_valid
end

it "should validate path" do
@checker.options[:path] = 'notarealpath/itreallyisnt'
@checker.should_not be_valid
end

it "should validate path" do
@checker.options[:path] = '/'
@checker.should be_valid
end
end

describe "#working?" do
it "generating events as scheduled" do
stub(@checker).run_command(@valid_path, 'pwd') { ["fake pwd output", "", 0] }

@checker.should_not be_working
@checker.check
@checker.reload.should be_working
three_days_from_now = 3.days.from_now
stub(Time).now { three_days_from_now }
@checker.should_not be_working
end
end

describe "#check" do
before do
stub(@checker).run_command(@valid_path, 'pwd') { ["fake pwd output", "", 0] }
end

it "should create an event when checking" do
expect { @checker.check }.to change { Event.count }.by(1)
Event.last.payload[:path].should == @valid_path
Event.last.payload[:command].should == 'pwd'
Event.last.payload[:output].should == "fake pwd output"
end

it "does not run when should_run? is false" do
stub(Agents::ShellCommandAgent).should_run? { false }
expect { @checker.check }.not_to change { Event.count }
end
end

describe "#receive" do
before do
stub(@checker).run_command(@valid_path, @event.payload[:command]) { ["fake ls output", "", 0] }
end

it "creates events" do
@checker.receive([@event])
Event.last.payload[:path].should == @valid_path
Event.last.payload[:command].should == @event.payload[:command]
Event.last.payload[:output].should == "fake ls output"
end

it "does not run when should_run? is false" do
stub(Agents::ShellCommandAgent).should_run? { false }

expect {
@checker.receive([@event])
}.not_to change { Event.count }
end
end
end

0 comments on commit a67231f

Please sign in to comment.