Skip to content

Security: require auth on the engine and fix host setup, release 0.12.0 - #32

Open
gkosmo wants to merge 7 commits into
masterfrom
fix/host-safety
Open

gkosmo wants to merge 7 commits into
masterfrom
fix/host-safety

Conversation

@gkosmo

@gkosmo gkosmo commented Nov 5, 2026 •

Copy link
Copy Markdown
Owner

Security fix. On every released version, the dashboard and JSON API have no access control: anyone who can reach the mount can read, create and run crews. Upgrade, or keep the mount behind your own authentication.

Summary

 install
-  rails generate rcrewai:rails:install     # generator not found
+  rails generate rcrewai:rails:install     # works

 every engine controller, API included
+  config.authenticate_with                 # required; 403 outside development without it

 tools
-  tool_class.constantize
+  checked against config.allowed_tool_classes   # empty by default

 dashboard layout
-  host application.js and application.css  # crashes on hosts without them
+  served by the engine, no asset pipeline needed

 failed runs
-  retried 3 times, each a new execution
+  no automatic retry unless the host opts in

 <mount>/cable                              # removed, outside the auth hook and unused

Also: config.agent_mcp_tools gives engine agents MCP tools from host code. Execution, agent and task pages that always crashed now render.

Supersedes #31, which is 0.11 tracking work; its docs change removing the temperature = nil workaround still needs carrying over or dropping.

Evidence

  • Before: rails generate rcrewai:rails:install printed Could not find generator. Anonymous requests reached every route. A host with no Sprockets application.js crashed on the dashboard.
    After: the real command works, tested on generated hosts with and without Sprockets. Anonymous requests are refused on every route, including the API. In a throwaway host with Playwright, Turbo loaded and a real broadcast updated the trace page live.
  • bundle exec rspec: 401 examples, 0 failures on Ruby 3.1.4, Rails 7.2.4 and rcrewai 0.11.0. It also passed on Ruby 3.4.1 and Rails 8.1.4.1.
  • CI is red on Ruby 3.3: host_app_spec.rb:93 (Sprockets host) fails on CI's Ruby 3.3.13 with cannot load such file -- sprockets/railtie. It failed again on a rerun. Both host specs pass locally on Ruby 3.3.11 with Rails 8.1.4.1, and CI passes on 3.1 and 3.2. The cause isn't known yet, so it isn't confirmed to be test setup only.
  • Not tested: a host using the actual webpacker gem (simulated by hosts without Sprockets), or any live LLM API.

Merge Danger

Door: one-way once published to RubyGems, though you can release a further version.

Blast Radius: breaking

  • 403 outside development until authenticate_with is set.
  • Tools raise NotAllowed until listed in allowed_tool_classes.
  • No automatic retries, so a transient error now fails the run.
  • <mount>/cable is gone.
  • rcrewai below 0.11 is no longer allowed. The approval fix is in rcrewai 0.12.0, so raise the minimum to 0.12 once that's on RubyGems.

Known issues, not fixed here

  • With checkpointing, a retry skips finished tasks, but tools in the failed task run again.
  • Engine records can't set require_approval_for_tools.
  • ActiveRecordTool, ActiveStorageTool and ActionMailerTool pass LLM-supplied names to constantize.
  • inflect.acronym 'AI' applies across the host app.

🤖 Generated with Claude Code

…names

The class namespace inflected to rcrew_a_i while the files live under rcrewai, so
'rails generate rcrewai:rails:install' from the README found nothing, and exited 0.
Neither did the rcrew_a_i name it suggested. Each generator now declares its name.
…Cable mount

Anyone who could reach the mount could read, create and run crews, through the
dashboard or the JSON API. config.authenticate_with now runs before every engine
controller. Truthy lets the request through, anything else is a 403 unless the
hook responded itself, and with no hook only development is allowed.

The engine also mounted Action Cable at <mount>/cable. Rack mounts skip the hook,
and nothing used it, since Turbo connects to the host's own endpoint. It's gone.
The layout linked the host's application.js and application.css, so it raised on a
Sprockets host whose manifest has no application.js, such as one on Webpacker, and
loaded the host's styles and scripts elsewhere. A controller now serves a dashboard
stylesheet and the Turbo build from turbo-rails, so no asset pipeline is involved,
and the page tells Turbo where the host's Action Cable is. Live updates are skipped,
not crashed on, when the host has no Action Cable.

Getting real pages to render also meant fixing views that always crashed: execution,
agent and task pages named columns and a route that don't exist. Delete links relied
on rails-ujs, which nothing loads, so they now use Turbo.
Tool class names from form fields and stored rows went straight to constantize.
They're now compared with config.allowed_tool_classes first, which is empty by
default. A disallowed class can't be saved and raises at run time rather than
letting the agent run without it.

Agent#instantiated_tools treated Tool records as JSON and crashed for any agent with
a dashboard-configured tool. It now builds the agent's active tools, and adds what
config.agent_mcp_tools returns. The host connects MCP servers itself, because a
stdio server starts a process, which shouldn't be reachable from a form.
CrewExecutionJob retried on any error, three times, and each attempt repeated the
tools. TaskExecutionJob and the core gem's Task retried as well. A retried run could
send the same email three times. All of that is now off by default.

config.crew_job_attempts turns job retries on. With a checkpoint to resume from, a
retry skips finished tasks and their tools. Tools in the task that failed still run
again, and without checkpointing the whole run repeats. This also replaces
wait: :exponentially_longer, which Rails has removed.
Allows rcrewai >= 0.11.0, < 0.13, and moves the default model off gpt-4, which OpenAI
shut down on 2026-10-23. Documents the security settings in the README and generated
initializer, and adds specs that generate real host apps, with and without Sprockets,
and run the documented commands in them.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant