Skip to content
BETAGuardrails are in beta. The library, the configuration format and the CLI command can still change.

Guardrails / ruby

Gems come from one trusted source

ruby/single-gem-source@v1

The Gemfile resolves gems from at most one source, and that source is one the repository trusts. The Gemfile is Ruby rather than a declaration and is read by pattern, so a second source that is found is a real finding, but finding one source is not proof that there is only one: a source named inside a condition, a loop or an eval is not seen.

Idruby/single-gem-source
Versionv1
Categoryruby
Default severityerror
Interpreterpython3
Timeout30 seconds
Violations tolerated0
Collectsruby

Collectors

This guardrail gathers nothing itself. It depends on the collectors below, which the CLI runs once per build before any check, and reads what they found out of GUARDRAIL_FACTS. A collector that collects nothing skips this guardrail rather than failing it.

CollectorGathersInputs it is given
rubyThe Bundler project in the project directory: the Ruby version it asks for, the gem sources it resolves from, the gems each manifest declares with their groups and origins, and the lock file beside them. The Gemfile is Ruby rather than a declaration, so what it declares conditionally is not seen and scanned says so.projectDir

The inputs above are this guardrail's own, passed straight through. Configuring one in buildnote.json changes what is collected, and two guardrails configured the same way share the one collection.

Configuration

json
{
  "guardrails": {
      "failOn": "error",
      "comment": true,
      "checks": [
          {
              "use": "ruby/single-gem-source@v1",
              "severity": "error",
              "with": {
                  "projectDir": ".",
                  "allowedSources": "https://rubygems.org"
              },
              "exemptions": []
          }
      ]
  }
}

Inputs

InputDescriptionDefaultEnvironment variable
projectDirDirectory holding the project, relative to the directory the CLI runs in..GUARDRAIL_INPUT_PROJECTDIR
allowedSourcesComma separated gem sources the repository trusts. A source outside this list is a violation; a trailing slash and letter case are ignored when comparing.https://rubygems.orgGUARDRAIL_INPUT_ALLOWEDSOURCES

How to fix

Keep one source line at the top of the Gemfile and name the other source on the gems that need it rather than on the whole build:

ruby
source "https://rubygems.org"

gem "acme-internal", source: "https://gems.internal.example.com"

With two sources for the whole Gemfile, Bundler resolves each gem from whichever of them offers the higher version, so anybody who can publish acme-internal to the public index takes over the install without touching this repository. That is what a dependency confusion attack needs.

More in ruby

  • ruby/lockfile-committed. The project commits Gemfile.lock, so an install of the same commit resolves the same gem versions.
  • ruby/no-git-dependencies. No gem the manifests declare is fetched from a git repository rather than from a gem source. The Gemfile is Ruby rather than a declaration and is read by pattern, so a git gem that is found is a real finding, but finding none is not proof that there is none: a gem added inside a condition, a loop or an eval is not seen.
  • ruby/version-declared. The project declares the Ruby version it is built and run against, and that version is no older than the configured floor.

All 4 ruby guardrails

Buildnote Limited
Registered in England and Wales, Reg: 16140412