Class: Straycall::Policy::Filesystem

Inherits:
Object
  • Object
show all
Defined in:
lib/straycall/policy/filesystem.rb

Constant Summary collapse

AT_FDCWD =
-100
WRITE_FLAGS =
Fcntl::O_WRONLY | Fcntl::O_RDWR | Fcntl::O_CREAT | Fcntl::O_TRUNC | Fcntl::O_APPEND

Instance Method Summary collapse

Constructor Details

#initialize(cache_size: 4096) ⇒ Filesystem

Returns a new instance of Filesystem.

Raises:

  • (ArgumentError)


11
12
13
14
15
16
17
18
19
20
# File 'lib/straycall/policy/filesystem.rb', line 11

def initialize(cache_size: 4096)
  raise ArgumentError, "cache_size must be positive" unless cache_size.is_a?(Integer) && cache_size.positive?

  @write_default = :allow
  @write_roots = []
  @read_denials = []
  @cache_size = cache_size
  @cache = {}
  @mutex = Mutex.new
end

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)


37
38
39
# File 'lib/straycall/policy/filesystem.rb', line 37

def active?
  @write_default == :deny || !@read_denials.empty?
end

#allow_write_under(path) ⇒ Object



22
23
24
25
# File 'lib/straycall/policy/filesystem.rb', line 22

def allow_write_under(path)
  @write_roots << normalize_config_path(path)
  clear_cache
end

#allowed_write?(path) ⇒ Boolean

Returns:

  • (Boolean)


49
50
51
52
53
# File 'lib/straycall/policy/filesystem.rb', line 49

def allowed_write?(path)
  cached([:write, path]) do
    @write_default == :allow || path.match?(%r{\A/proc/(?:self|\d+)/task/\d+/comm\z}) || @write_roots.any? { |root| under?(path, root) }
  end
end

#denied_read?(path) ⇒ Boolean

Returns:

  • (Boolean)


55
56
57
# File 'lib/straycall/policy/filesystem.rb', line 55

def denied_read?(path)
  cached([:read, path]) { @read_denials.any? { |root| under?(path, root) } }
end

#deny_read(path) ⇒ Object



32
33
34
35
# File 'lib/straycall/policy/filesystem.rb', line 32

def deny_read(path)
  @read_denials << normalize_config_path(path)
  clear_cache
end

#deny_write_elsewhere!Object



27
28
29
30
# File 'lib/straycall/policy/filesystem.rb', line 27

def deny_write_elsewhere!
  @write_default = :deny
  clear_cache
end

#marshal_dumpObject



72
73
74
# File 'lib/straycall/policy/filesystem.rb', line 72

def marshal_dump
  [@write_default, @write_roots, @read_denials, @cache_size]
end

#marshal_load(values) ⇒ Object



76
77
78
79
80
# File 'lib/straycall/policy/filesystem.rb', line 76

def marshal_load(values)
  @write_default, @write_roots, @read_denials, @cache_size = values
  @cache = {}
  @mutex = Mutex.new
end

#read_denials?Boolean

Returns:

  • (Boolean)


41
42
43
# File 'lib/straycall/policy/filesystem.rb', line 41

def read_denials?
  !@read_denials.empty?
end

#resolve(path, tid:, dirfd: AT_FDCWD) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/straycall/policy/filesystem.rb', line 59

def resolve(path, tid:, dirfd: AT_FDCWD)
  return File.expand_path(path) if path.start_with?(File::SEPARATOR)

  base = if signed(dirfd) == AT_FDCWD
    File.readlink("/proc/#{tid}/cwd")
  else
    File.readlink("/proc/#{tid}/fd/#{signed(dirfd)}")
  end.delete_suffix(" (deleted)")
  File.expand_path(path, base)
rescue SystemCallError => error
  raise PathResolutionError, "cannot resolve target path through /proc: #{error.message}"
end

#write?(flags) ⇒ Boolean

Returns:

  • (Boolean)


45
46
47
# File 'lib/straycall/policy/filesystem.rb', line 45

def write?(flags)
  (flags & WRITE_FLAGS).positive?
end