> ## Documentation Index
> Fetch the complete documentation index at: https://tally.wharflab.com/llms.txt
> Use this file to discover all available pages before exploring further.

# tally/ruby/secrets-in-arg-or-env

> Rails secret declared via ARG/ENV bakes the secret into image history.

A Rails secret name (`SECRET_KEY_BASE`, `RAILS_MASTER_KEY`, `DEVISE_SECRET_KEY`, etc.) is declared via `ARG`
or `ENV` with a non-placeholder value, baking the secret into image history.

| Property | Value                     |
| -------- | ------------------------- |
| Severity | Error                     |
| Category | Security                  |
| Default  | Enabled                   |
| Auto-fix | No edit (suggestion only) |

## Description

Rails secrets declared via `ARG` or `ENV` end up in image history. `docker history --no-trunc <image>` and any
registry that proxies image manifests will leak them. Anyone who can pull the image can recover the secret.

This rule fires on the following env-var names — the Rails core canonical secrets plus Devise's:

* `SECRET_KEY_BASE`
* `RAILS_MASTER_KEY`
* `SECRET_TOKEN` (legacy Rails 4.x)
* `DEVISE_SECRET_KEY`
* `DEVISE_PEPPER`
* `RAILS_KEY` (alternate alias seen in some apps)

The rule fires for both `ARG` and `ENV`, including:

* `ENV SECRET_KEY_BASE="abc123"` — value baked into image layer history.
* `ARG SECRET_KEY_BASE=value` — value baked into both layer history (when consumed via `RUN`) and the build
  cache key data.
* `ARG SECRET_KEY_BASE` (no default) — still flagged. The user is meant to pass `--build-arg SECRET_KEY_BASE=...`,
  which adds the secret to build cache key data and (when consumed via `RUN`/`ENV`) leaks it into image
  history.
* Meta-`ARG` (before any `FROM`).

### Compliance

The rule treats `=1`, `="1"`, `="dummy"`, and `="SECRET_KEY_BASE_DUMMY"` (case-insensitive) as compliant
placeholder values — these match Rails 7.1+'s build-time placeholder contract for asset compilation.
`SECRET_KEY_BASE_DUMMY=1` is the supported way to run `bin/rails assets:precompile` without a real key.

Stages explicitly named `dev`/`development`/`test`/`testing`/`ci`/`debug` are skipped.

### Recommended replacement

Use BuildKit's secret-mount syntax: secrets exist for the duration of one `RUN`, never appear in image
content, and are not part of the cache key.

```dockerfile theme={null}
RUN --mount=type=secret,id=rails_master_key,env=RAILS_MASTER_KEY \
    bundle exec rails db:schema:load
```

For asset compilation specifically, `SECRET_KEY_BASE_DUMMY=1` (Rails 7.1+) avoids needing the real key at
build time entirely:

```dockerfile theme={null}
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
```

This is also caught by [`tally/ruby/asset-precompile-without-dummy-key`](/rules/tally/ruby/asset-precompile-without-dummy-key/),
which is the structural fix for build-time `SECRET_KEY_BASE`.

## Examples

### Before

```dockerfile theme={null}
FROM ruby:3.3-slim
ARG RAILS_MASTER_KEY
ENV RAILS_MASTER_KEY=$RAILS_MASTER_KEY
RUN bundle exec rails db:schema:load
```

### After

```dockerfile theme={null}
FROM ruby:3.3-slim
RUN --mount=type=secret,id=rails_master_key,env=RAILS_MASTER_KEY \
    bundle exec rails db:schema:load
```

Pass the secret at build time with:

```bash theme={null}
docker buildx build --secret id=rails_master_key,src=$HOME/.ssh/rails_master_key .
```

## Auto-fix

The rule emits a no-edit `FixUnsafe` suggestion: the description points the user at the BuildKit secret-mount
pattern. The actual rewrite depends on the user's CI / vault / secret-injection setup, so the rule cannot
auto-rewrite.

## References

* [Rails security guide — environmental security](https://guides.rubyonrails.org/security.html#environmental-security).
* [BuildKit `RUN --mount=type=secret` documentation](https://docs.docker.com/build/building/secrets/).
* [Rails 7.1 release notes — `SECRET_KEY_BASE_DUMMY`](https://guides.rubyonrails.org/7_1_release_notes.html#assets-precompile-no-longer-requires-credentials).
