Skip to content

Engine: Ignoring vendor directory considered harmful - #1508

Open
joelhawksley wants to merge 1 commit into
marcoroth:mainfrom
joelhawksley:vendor-footgun
Open

Engine: Ignoring vendor directory considered harmful#1508
joelhawksley wants to merge 1 commit into
marcoroth:mainfrom
joelhawksley:vendor-footgun

Conversation

@joelhawksley

@joelhawksley joelhawksley commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

By default, Herb currently ignores the vendor directly. This is potentially quite harmful, as it could expose consuming applications to runtime exceptions.

For example. We vendor https://github.com/primer/view_components in our application. The dependency includes ViewComponents with ERB files, including a few that will not compile with Herb. If we don't compile the templates in our test environment, those Herb compilation errors will not be caught and would raise in production.

I see similar risks for other gems that provide UI, such as as dashboards mounted via an engine.

I'm wary of blankly excluding the directory silently by default, especially when we describe the behavior of herb analyze as Running herb analyze without arguments now defaults to the current directory (https://herb-tools.dev/blog/whats-new-in-herb-v0-9#improved-herb-analyze-command). This could make one believe that all subdirectories are being analyzed based on the config in the user's .herb.yml file, but in fact it is the combination of defaults.yml and the user's configuration.

It also appears that adding 'vendor/**/*.erb' to include still results in the directory being excluded. I had to explicitly call bundle exec herb analyze vendor to see errors from the vendor folder!

Of course, this does nothing to avoid similar issues for gems that provide UI that are not vendored. I believe we should include a boot-time safe-guard for this issue in ReActionView at the very least. Or, we could look into ways of not using Herb to compile ERB from gems, but that sounds like a mess to me.

By default, Herb currently ignores the `vendor` directly. This is potentially quite harmful, as it could expose consuming applications to runtime exceptions.

For example. We vendor https://github.com/primer/view_components in our application. The dependency includes ViewComponents with ERB files, including a few that will not compile with Herb. If we don't compile the templates in our test environment, those Herb compilation errors will not be caught.

I see similar risks for other gems that provide UI, such as as dashboards mounted via an engine.

We thankfully discovered this issue and manually added `vendor/**/*.erb` to our `include` configuration, but I'm wary of blankly excluding the directory silently by default, especially when we describe the behavior of `herb analyze` as `Running herb analyze without arguments now defaults to the current directory`. This could make one believe that all subdirectories are being analyzed based on the config in the user's `.herb.yml` file, but in fact it is the combination of `defaults.yml` and the user's configuration.

Signed-off-by: Joel Hawksley <joelhawksley@github.com>
@github-actions github-actions Bot added the rubygem The herb RubyGem and its packaging label Mar 27, 2026
@joelhawksley
joelhawksley marked this pull request as ready for review March 27, 2026 16:05
@marcoroth marcoroth changed the title Ignoring vendor directory considered harmful Engine: Ignoring vendor directory considered harmful May 22, 2026
marcoroth added a commit to marcoroth/reactionview that referenced this pull request Aug 8, 2026
Follow up on #94.

This pull request adds `config.external_template_mode` to control what
happens to templates that come from gems rather than from the
application itself.

#### Motivation

With `intercept_erb` enabled, ReActionView sees every `.html.erb`
template Rails renders, including ones shipped inside gems. #94 stopped
compiling those, which fixed #91, but it did so silently. If a gem's
templates cannot be compiled by Herb, you never find out.

That silence is the objection @joelhawksley raises in
marcoroth/herb#1508, where a vendored copy of `primer/view_components`
contains ERB that Herb cannot compile:

So rather than a binary "skip or don't", the mode says how loudly to
handle the failure:

| Mode | Behavior |
| --- | --- |
| `:fallback` (default) | Compile with Herb. If that fails, log a
warning and fall back to `ActionView::Template::Handlers::ERB`, so the
template renders exactly as it would without ReActionView installed. |
| `:skip` | Never compile templates that come from gems. This is what
#94 shipped. |
| `:compile` | No special treatment. Your `validation_mode` applies to
them just as it does to your own templates, and nothing is rescued. |

```ruby
ReActionView.configure do |config|
  config.external_template_mode = :fallback
end
```

A gem template Herb cannot handle now renders as before, and says so:

```
[ReActionView] /app/vendor/bundle/ruby/3.4.0/gems/actionpack-8.1.2/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb
could not be compiled by Herb, falling back to ActionView::Template::Handlers::ERB:
InvalidNestingError: Block element <h2> cannot be nested inside <p> at line 9
```

Related marcoroth/herb#1508
Related marcoroth/herb#1362
Related #91
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rubygem The herb RubyGem and its packaging

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant