VYPR
Low severity3.3NVD Advisory· Published May 2, 2017· Updated May 13, 2026

CVE-2017-8418

CVE-2017-8418

Description

RuboCop 0.48.1 and earlier does not use /tmp in safe way, allowing local users to exploit this to tamper with cache files belonging to other users.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
rubocopRubyGems
< 0.49.00.49.0

Affected products

1

Patches

1
dcb258fabd5f

[Fix #4336] Store rubocop_cache in safer directories

https://github.com/rubocop/rubocopJonas ArvidssonMay 7, 2017via ghsa
5 files changed · +72 34
  • CHANGELOG.md+1 0 modified
    @@ -17,6 +17,7 @@
     * [#3400](https://github.com/bbatsov/rubocop/issues/3400): Remove auto-correct support from Lint/Debugger. ([@ilansh][])
     * [#4278](https://github.com/bbatsov/rubocop/pull/4278): Move all cops dealing with whitespace into a new department called `Layout`. ([@jonas054][])
     * [#4320](https://github.com/bbatsov/rubocop/pull/4320): Update `Rails/OutputSafety` to disallow wrapping `raw` or `html_safe` with `safe_join`. ([@klesse413][])
    +* [#4336](https://github.com/bbatsov/rubocop/issues/4336): Store `rubocop_cache` in safer directories. ([@jonas054][])
     
     ### Bug fixes
     
    
  • config/default.yml+11 12 modified
    @@ -95,18 +95,17 @@ AllCops:
       # Threshold for how many files can be stored in the result cache before some
       # of the files are automatically removed.
       MaxFilesInCache: 20000
    -  # The cache will be stored in "rubocop_cache" under this directory. The name
    -  # "/tmp" is special and will be converted to the system temporary directory,
    -  # which is "/tmp" on Unix-like systems, but could be something else on other
    -  # systems.
    -  CacheRootDirectory: /tmp
    -  # The default cache root directory is /tmp, which on most systems is
    -  # writable by any system user. This means that it is possible for a
    -  # malicious user to anticipate the location of Rubocop's cache directory,
    -  # and create a symlink in its place that could cause Rubocop to overwrite
    -  # unintended files, or read malicious input. If you are certain that your
    -  # cache location is secure from this kind of attack, and wish to use a
    -  # symlinked cache location, set this value to "true".
    +  # The cache will be stored in "rubocop_cache" under this directory. If
    +  # CacheRootDirectory is ~ (nil), which it is by default, the root will be
    +  # taken from the environment variable `$XDG_CACHE_HOME` if it is set, or if
    +  # `$XDG_CACHE_HOME` is not set, it will be `$HOME/.cache/`.
    +  CacheRootDirectory: ~
    +  # It is possible for a malicious user to know the location of RuboCop's cache
    +  # directory by looking at CacheRootDirectory, and create a symlink in its
    +  # place that could cause RuboCop to overwrite unintended files, or read
    +  # malicious input. If you are certain that your cache location is secure from
    +  # this kind of attack, and wish to use a symlinked cache location, set this
    +  # value to "true".
       AllowSymlinksInCacheRootDirectory: false
       # What MRI version of the Ruby interpreter is the inspected code intended to
       # run on? (If there is more than one, set this to the lowest version.)
    
  • lib/rubocop/result_cache.rb+7 6 modified
    @@ -2,7 +2,6 @@
     
     require 'digest/md5'
     require 'find'
    -require 'tmpdir'
     require 'etc'
     
     module RuboCop
    @@ -63,11 +62,13 @@ def remove_files(files, dirs, remove_count)
     
         def self.cache_root(config_store)
           root = config_store.for('.').for_all_cops['CacheRootDirectory']
    -      if root == '/tmp'
    -        tmpdir = File.realpath(Dir.tmpdir)
    -        # Include user ID in the path to make sure the user has write access.
    -        root = File.join(tmpdir, Process.uid.to_s)
    -      end
    +      root ||= if ENV.key?('XDG_CACHE_HOME')
    +                 # Include user ID in the path to make sure the user has write
    +                 # access.
    +                 File.join(ENV['XDG_CACHE_HOME'], Process.uid.to_s)
    +               else
    +                 File.join(ENV['HOME'], '.cache')
    +               end
           File.join(root, 'rubocop_cache')
         end
     
    
  • manual/caching.md+9 8 modified
    @@ -30,14 +30,15 @@ overrides the setting.
     
     ### Cache Path
     
    -By default, the cache is stored in a subdirectory of the temporary
    -directory, `/tmp/rubocop_cache/` on Unix-like systems. The
    -configuration parameter `AllCops: CacheRootDirectory` can be used to
    -set it to a different path. One reason to use this option could be
    -that there's a network disk where users on different machines want to
    -have a common RuboCop cache. Another could be that a Continuous
    -Integration system allows directories, but not a temporary directory,
    -to be saved between runs.
    +By default, the cache is stored in either
    +`$XDG_CACHE_HOME/rubocop_cache` if `$XDG_CACHE_HOME` is set, or in
    +`$HOME/.cache/rubocop_cache/` if it's not.  The configuration parameter
    +`AllCops: CacheRootDirectory` can be used to set the root to a
    +different path. One reason to use this option could be that there's a
    +network disk where users on different machines want to have a common
    +RuboCop cache. Another could be that a Continuous Integration system
    +allows directories, but not a temporary directory, to be saved between
    +runs.
     
     ### Cache Pruning
     
    
  • spec/rubocop/result_cache_spec.rb+44 8 modified
    @@ -197,20 +197,56 @@ def abs(path)
         end
       end
     
    -  context 'the cache path when using a temp directory' do
    +  describe 'the cache path' do
         let(:config_store) { double('config_store') }
    -    let(:tmpdir) { File.realpath(Dir.tmpdir) }
         let(:puid) { Process.uid.to_s }
     
         before do
    -      allow(config_store).to receive(:for).with('.').and_return(
    -        RuboCop::Config.new('AllCops' => { 'CacheRootDirectory' => '/tmp' })
    -      )
    +      all_cops = {
    +        'AllCops' => { 'CacheRootDirectory' => cache_root_directory }
    +      }
    +      config = RuboCop::Config.new(all_cops)
    +      allow(config_store).to receive(:for).with('.').and_return(config)
         end
     
    -    it 'contains the process uid' do
    -      cacheroot = RuboCop::ResultCache.cache_root(config_store)
    -      expect(cacheroot).to eq(File.join(tmpdir, puid, 'rubocop_cache'))
    +    context 'when CacheRootDirectory not set' do
    +      let(:cache_root_directory) { nil }
    +
    +      context 'and XDG_CACHE_HOME is not set' do
    +        before { ENV['XDG_CACHE_HOME'] = nil }
    +
    +        it 'contains $HOME/.cache' do
    +          cacheroot = RuboCop::ResultCache.cache_root(config_store)
    +          expect(cacheroot)
    +            .to eq(File.join(Dir.home, '.cache', 'rubocop_cache'))
    +        end
    +      end
    +
    +      context 'and XDG_CACHE_HOME is set' do
    +        around do |example|
    +          ENV['XDG_CACHE_HOME'] = '/etc/rccache'
    +          begin
    +            example.run
    +          ensure
    +            ENV.delete('XDG_CACHE_HOME')
    +          end
    +        end
    +
    +        it 'contains the given path and UID' do
    +          cacheroot = RuboCop::ResultCache.cache_root(config_store)
    +          expect(cacheroot)
    +            .to eq(File.join(ENV['XDG_CACHE_HOME'], puid, 'rubocop_cache'))
    +        end
    +      end
    +    end
    +
    +    context 'when CacheRootDirectory is set' do
    +      let(:cache_root_directory) { '/opt' }
    +
    +      it 'contains the given root' do
    +        cacheroot = RuboCop::ResultCache.cache_root(config_store)
    +        expect(cacheroot).to eq(File.join('/opt', 'rubocop_cache'))
    +      end
         end
       end
     end
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.