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.
| Package | Affected versions | Patched versions |
|---|---|---|
rubocopRubyGems | < 0.49.0 | 0.49.0 |
Affected products
1Patches
1dcb258fabd5f[Fix #4336] Store rubocop_cache in safer directories
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- www.openwall.com/lists/oss-security/2017/05/01/14nvdExploitMailing ListThird Party AdvisoryWEB
- github.com/bbatsov/rubocop/issues/4336nvdExploitThird Party AdvisoryWEB
- github.com/advisories/GHSA-wmjf-jpjj-9f3jghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2017-8418ghsaADVISORY
- github.com/rubocop/rubocop/commit/dcb258fabd5f2624c1ea0e1634763094590c09d7ghsaWEB
- github.com/rubysec/ruby-advisory-db/blob/master/gems/rubocop/CVE-2017-8418.ymlghsaWEB
News mentions
0No linked articles in our index yet.