Class: Rigor::Plugin::TrustPolicy

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/plugin/trust_policy.rb

Overview

Declarative trust / I/O policy for the active plugin set. Pinned by [ADR-2 § “Plugin Trust and I/O Policy”](../../../docs/adr/2-extension-api.md): plugins are *trusted Ruby gems selected by the user, their Gemfile, or project configuration*; this class is the programmatic surface that documents that trust and lets the analyzer enforce read scope + network disablement at the documented edges.

The policy is **not a sandbox.** A plugin that uses raw ‘File.read` or `Net::HTTP` bypasses the policy — ADR-2 explicitly chooses documentation over forced isolation. The contract is: when plugins go through IoBoundary (the analyzer-side helper service slice 2 introduces), the boundary checks against this policy and feeds compliant reads into the cache descriptor for invalidation. Slices 3-6 wire plugin contributions through the boundary so the policy is the actual mechanism, not just paperwork.

## Fields

  • ‘trusted_gems`: gem names the user has authorised. Derived from the `plugins:` section of `.rigor.yml` plus any gems they reach transitively. Used today for documentation and future trust diagnostics.

  • ‘allowed_read_roots`: absolute paths plugin code may read from through the IoBoundary. The default set covers the project root, the project’s ‘signature_paths`, the active `Gemfile.lock`, and each trusted gem’s ‘Gem::Specification#full_gem_path`. The user extends this with `.rigor.yml`’s ‘plugins_io.allowed_paths:`.

  • ‘network_policy`: one of VALID_NETWORK_POLICIES. `:disabled` (default) makes IoBoundary#open_url always raise. `:allowlist` (v0.1.2) consults `allowed_url_hosts` on every fetch — the hostname must be on the list and the URL scheme MUST be `https`. The list of allowed hosts is exact-match (no wildcards in v0.1.2).

Constant Summary collapse

VALID_NETWORK_POLICIES =
%i[disabled allowlist].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(trusted_gems: [], allowed_read_roots: [], network_policy: :disabled, allowed_url_hosts: []) ⇒ TrustPolicy

Returns a new instance of TrustPolicy.



46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/rigor/plugin/trust_policy.rb', line 46

def initialize(trusted_gems: [], allowed_read_roots: [], network_policy: :disabled, allowed_url_hosts: [])
  validate_network_policy!(network_policy)

  @trusted_gems = trusted_gems.map { |g| g.to_s.dup.freeze }.uniq.sort.freeze
  @allowed_read_roots = allowed_read_roots
                        .map { |path| File.expand_path(path).freeze }
                        .uniq
                        .sort
                        .freeze
  @network_policy = network_policy
  @allowed_url_hosts = allowed_url_hosts.map { |h| h.to_s.downcase.dup.freeze }.uniq.sort.freeze
  freeze
end

Instance Attribute Details

#allowed_read_rootsObject (readonly)

Returns the value of attribute allowed_read_roots.



44
45
46
# File 'lib/rigor/plugin/trust_policy.rb', line 44

def allowed_read_roots
  @allowed_read_roots
end

#allowed_url_hostsObject (readonly)

Returns the value of attribute allowed_url_hosts.



44
45
46
# File 'lib/rigor/plugin/trust_policy.rb', line 44

def allowed_url_hosts
  @allowed_url_hosts
end

#network_policyObject (readonly)

Returns the value of attribute network_policy.



44
45
46
# File 'lib/rigor/plugin/trust_policy.rb', line 44

def network_policy
  @network_policy
end

#trusted_gemsObject (readonly)

Returns the value of attribute trusted_gems.



44
45
46
# File 'lib/rigor/plugin/trust_policy.rb', line 44

def trusted_gems
  @trusted_gems
end

Instance Method Details

#allow_read?(path) ⇒ Boolean

Returns true when the absolute path falls inside any allowed read root. Symlinks are resolved through ‘File.expand_path` only (no `realpath`); plugins with adversarial intent are out of scope per ADR-2.

Parameters:

  • path (String)

Returns:

  • (Boolean)

    true when the absolute path falls inside any allowed read root. Symlinks are resolved through ‘File.expand_path` only (no `realpath`); plugins with adversarial intent are out of scope per ADR-2.



65
66
67
68
# File 'lib/rigor/plugin/trust_policy.rb', line 65

def allow_read?(path)
  absolute = File.expand_path(path.to_s)
  @allowed_read_roots.any? { |root| inside?(absolute, root) }
end

#allow_url?(url) ⇒ Boolean

Returns true when the URL scheme is ‘https` and the parsed hostname is in `allowed_url_hosts`. Always `false` while `network_policy` is `:disabled`.

Parameters:

  • url (String, URI)

Returns:

  • (Boolean)

    true when the URL scheme is ‘https` and the parsed hostname is in `allowed_url_hosts`. Always `false` while `network_policy` is `:disabled`.



78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/rigor/plugin/trust_policy.rb', line 78

def allow_url?(url)
  return false if @network_policy == :disabled
  return false if @allowed_url_hosts.empty?

  require "uri"
  uri = url.is_a?(URI::Generic) ? url : URI.parse(url.to_s)
  return false unless uri.is_a?(URI::HTTPS)
  return false if uri.host.nil?

  @allowed_url_hosts.include?(uri.host.downcase)
rescue URI::InvalidURIError
  false
end

#gem_trusted?(name) ⇒ Boolean

Returns:

  • (Boolean)


92
93
94
# File 'lib/rigor/plugin/trust_policy.rb', line 92

def gem_trusted?(name)
  @trusted_gems.include?(name.to_s)
end

#network_allowed?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/rigor/plugin/trust_policy.rb', line 70

def network_allowed?
  @network_policy != :disabled
end

#to_hObject



96
97
98
99
100
101
102
103
# File 'lib/rigor/plugin/trust_policy.rb', line 96

def to_h
  {
    "trusted_gems" => trusted_gems,
    "allowed_read_roots" => allowed_read_roots,
    "network_policy" => network_policy.to_s,
    "allowed_url_hosts" => allowed_url_hosts
  }
end