reeve 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +191 -0
- data/README.md +280 -17
- data/lib/generators/reeve/install/install_generator.rb +46 -0
- data/lib/generators/reeve/install/templates/create_audit_entries.rb.tt +77 -0
- data/lib/generators/reeve/install/templates/initializer.rb.tt +65 -0
- data/lib/reeve/audit/entry.rb +54 -0
- data/lib/reeve/audit/query.rb +84 -0
- data/lib/reeve/audit/recorder.rb +181 -0
- data/lib/reeve/audit/redactor.rb +67 -0
- data/lib/reeve/audit.rb +97 -0
- data/lib/reeve/authorization/adapter.rb +71 -0
- data/lib/reeve/authorization/adapters/plain.rb +65 -0
- data/lib/reeve/authorization/adapters/pundit.rb +97 -0
- data/lib/reeve/authorization/authorizer.rb +23 -0
- data/lib/reeve/authorization/current.rb +66 -0
- data/lib/reeve/authorization/declaration.rb +73 -0
- data/lib/reeve/authorization/guard.rb +97 -0
- data/lib/reeve/authorization/registry.rb +118 -0
- data/lib/reeve/authorization/scoper.rb +399 -0
- data/lib/reeve/authorization.rb +76 -0
- data/lib/reeve/configuration.rb +158 -0
- data/lib/reeve/context.rb +111 -0
- data/lib/reeve/decision.rb +111 -0
- data/lib/reeve/errors.rb +89 -0
- data/lib/reeve/fast_mcp.rb +24 -0
- data/lib/reeve/integrations/fast_mcp/context_builder.rb +48 -0
- data/lib/reeve/integrations/fast_mcp/tool_extension.rb +55 -0
- data/lib/reeve/invocation.rb +260 -0
- data/lib/reeve/minitest.rb +19 -0
- data/lib/reeve/rspec.rb +18 -0
- data/lib/reeve/scope_result.rb +104 -0
- data/lib/reeve/testing/assertions.rb +76 -0
- data/lib/reeve/testing/checks/audit_coverage.rb +46 -0
- data/lib/reeve/testing/checks/base.rb +155 -0
- data/lib/reeve/testing/checks/contract_version.rb +97 -0
- data/lib/reeve/testing/checks/cross_principal_leak.rb +132 -0
- data/lib/reeve/testing/checks/guard_declared.rb +33 -0
- data/lib/reeve/testing/checks/principal_required.rb +60 -0
- data/lib/reeve/testing/checks/redaction_holds.rb +142 -0
- data/lib/reeve/testing/checks/rule_present.rb +55 -0
- data/lib/reeve/testing/checks.rb +95 -0
- data/lib/reeve/testing/compliance_assertions.rb +34 -0
- data/lib/reeve/testing/compliance_suite.rb +23 -0
- data/lib/reeve/testing/ledger.rb +82 -0
- data/lib/reeve/testing/matchers/audit_every_call.rb +39 -0
- data/lib/reeve/testing/matchers/base.rb +84 -0
- data/lib/reeve/testing/matchers/deny_access_for.rb +38 -0
- data/lib/reeve/testing/matchers/pass_reeve_check.rb +32 -0
- data/lib/reeve/testing/matchers.rb +33 -0
- data/lib/reeve/testing/report.rb +58 -0
- data/lib/reeve/testing/result.rb +49 -0
- data/lib/reeve/testing.rb +60 -0
- data/lib/reeve/version.rb +1 -1
- data/lib/reeve.rb +10 -3
- metadata +53 -2
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "matchers/base"
|
|
4
|
+
require_relative "matchers/deny_access_for"
|
|
5
|
+
require_relative "matchers/audit_every_call"
|
|
6
|
+
require_relative "matchers/pass_reeve_check"
|
|
7
|
+
|
|
8
|
+
module Reeve
|
|
9
|
+
module Testing
|
|
10
|
+
# The RSpec front-end.
|
|
11
|
+
#
|
|
12
|
+
# require "reeve/rspec"
|
|
13
|
+
# RSpec.configure { |c| c.include Reeve::Testing::Matchers }
|
|
14
|
+
#
|
|
15
|
+
# RSpec.describe InvoiceSearchTool do
|
|
16
|
+
# it { is_expected.to deny_access_for(stranger).with(query: "AC") }
|
|
17
|
+
# it { is_expected.to audit_every_call }
|
|
18
|
+
# end
|
|
19
|
+
module Matchers
|
|
20
|
+
def deny_access_for(principal)
|
|
21
|
+
DenyAccessFor.new(principal)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def audit_every_call
|
|
25
|
+
AuditEveryCall.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def pass_reeve_check
|
|
29
|
+
PassReeveCheck.new
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Reeve
|
|
4
|
+
module Testing
|
|
5
|
+
# The aggregate of one compliance run (FR-018).
|
|
6
|
+
#
|
|
7
|
+
# +to_s+ is written to be the whole output of a failing CI step: a one-line verdict
|
|
8
|
+
# followed by every failure in full. A report that has to be cross-referenced with
|
|
9
|
+
# something else is a report nobody reads at 3am.
|
|
10
|
+
class Report
|
|
11
|
+
attr_reader :results
|
|
12
|
+
|
|
13
|
+
def initialize(results)
|
|
14
|
+
@results = results.freeze
|
|
15
|
+
freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def passed?
|
|
19
|
+
failures.empty?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def failed?
|
|
23
|
+
!passed?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def failures
|
|
27
|
+
results.reject(&:passed?)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def passes
|
|
31
|
+
results.select(&:passed?)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def size
|
|
35
|
+
results.size
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def to_s
|
|
39
|
+
[summary, *failures.map { |result| detail(result) }].join("\n")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def inspect
|
|
43
|
+
"#<Reeve::Testing::Report #{summary}>"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def summary
|
|
49
|
+
"reeve compliance: #{size} #{size == 1 ? 'check' : 'checks'}, " \
|
|
50
|
+
"#{passes.size} passed, #{failures.size} failed"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def detail(result)
|
|
54
|
+
"\nFAIL #{result.check}\n #{result.message}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Reeve
|
|
4
|
+
module Testing
|
|
5
|
+
# What a check hands back: whether the guarantee held, the sentence explaining it, and
|
|
6
|
+
# the structured facts behind the sentence.
|
|
7
|
+
#
|
|
8
|
+
# The message is built here and nowhere else. A front-end — an RSpec matcher, a
|
|
9
|
+
# Minitest assertion, a rake task — reads +message+ and prints it; none of them
|
|
10
|
+
# composes their own (FR-019). That is what makes the same violation read identically
|
|
11
|
+
# from all three.
|
|
12
|
+
class Result
|
|
13
|
+
attr_reader :check, :message, :details
|
|
14
|
+
|
|
15
|
+
def self.passed(check:, message:, details: {})
|
|
16
|
+
new(check: check, passed: true, message: message, details: details)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.failed(check:, message:, details: {})
|
|
20
|
+
new(check: check, passed: false, message: message, details: details)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def initialize(check:, passed:, message:, details: {})
|
|
24
|
+
@check = check.to_s
|
|
25
|
+
@passed = passed ? true : false
|
|
26
|
+
@message = message.to_s
|
|
27
|
+
@details = details.freeze
|
|
28
|
+
freeze
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def passed?
|
|
32
|
+
@passed
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def failed?
|
|
36
|
+
!@passed
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_s
|
|
40
|
+
message
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def inspect
|
|
44
|
+
"#<Reeve::Testing::Result #{check} #{passed? ? 'passed' : 'failed'} " \
|
|
45
|
+
"#{message.inspect}>"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../reeve"
|
|
4
|
+
require_relative "testing/result"
|
|
5
|
+
require_relative "testing/report"
|
|
6
|
+
require_relative "testing/ledger"
|
|
7
|
+
require_relative "testing/checks"
|
|
8
|
+
|
|
9
|
+
module Reeve
|
|
10
|
+
# The testing kit: framework-neutral checks, and thin front-ends over them.
|
|
11
|
+
#
|
|
12
|
+
# require "reeve/testing" # the checks, and nothing else
|
|
13
|
+
# require "reeve/rspec" # + matchers and the shared example group
|
|
14
|
+
# require "reeve/minitest" # + assertions and the compliance test case
|
|
15
|
+
#
|
|
16
|
+
# This file loads no test framework and no ActiveRecord, so the checks are runnable from
|
|
17
|
+
# a rake task, a CI script or a boot-time assertion in staging (FR-020, FR-026):
|
|
18
|
+
#
|
|
19
|
+
# require "reeve/testing"
|
|
20
|
+
# report = Reeve::Checks.run_all(principals: [alice, bob])
|
|
21
|
+
# abort report.to_s unless report.passed?
|
|
22
|
+
module Testing
|
|
23
|
+
class << self
|
|
24
|
+
# The two fixture principals the compliance suite runs every tool against. Set once,
|
|
25
|
+
# in the host's test helper, and both front-ends' compliance suites pick it up:
|
|
26
|
+
#
|
|
27
|
+
# Reeve::Testing.compliance_principals = -> { [users(:alice), users(:bob)] }
|
|
28
|
+
#
|
|
29
|
+
# A callable rather than a value, because in a Rails test suite the fixtures do not
|
|
30
|
+
# exist yet at the moment the helper is loaded.
|
|
31
|
+
attr_writer :compliance_principals
|
|
32
|
+
|
|
33
|
+
def compliance_principals
|
|
34
|
+
source = @compliance_principals || Reeve.config.compliance_principals
|
|
35
|
+
raise ConfigurationError, missing_principals_message if source.nil?
|
|
36
|
+
|
|
37
|
+
principals = Array(source.respond_to?(:call) ? source.call : source)
|
|
38
|
+
raise ConfigurationError, missing_principals_message if principals.size < 2
|
|
39
|
+
|
|
40
|
+
principals
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def compliance_principals?
|
|
44
|
+
!(@compliance_principals || Reeve.config.compliance_principals).nil?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def reset!
|
|
48
|
+
@compliance_principals = nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def missing_principals_message
|
|
54
|
+
"the reeve compliance suite needs two fixture principals with disjoint records. " \
|
|
55
|
+
"Set them in your test helper:\n\n " \
|
|
56
|
+
"Reeve::Testing.compliance_principals = -> { [users(:alice), users(:bob)] }"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/reeve/version.rb
CHANGED
data/lib/reeve.rb
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "reeve/version"
|
|
4
|
+
require_relative "reeve/errors"
|
|
5
|
+
require_relative "reeve/decision"
|
|
6
|
+
require_relative "reeve/configuration"
|
|
7
|
+
require_relative "reeve/context"
|
|
8
|
+
require_relative "reeve/scope_result"
|
|
9
|
+
require_relative "reeve/invocation"
|
|
10
|
+
require_relative "reeve/authorization"
|
|
4
11
|
|
|
5
12
|
# Reeve — per-record authorization and an append-only audit ledger for the MCP tools
|
|
6
13
|
# a Rails application exposes to AI agents.
|
|
7
14
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
15
|
+
# A tool declares the policy that governs it with `guard_with`; every invocation is
|
|
16
|
+
# authorized, scoped to what the principal may see, and recorded. See
|
|
17
|
+
# https://github.com/vicmaster/reeve.
|
|
10
18
|
module Reeve
|
|
11
|
-
class Error < StandardError; end
|
|
12
19
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: reeve
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Victor Velazquez
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: |
|
|
14
14
|
Reeve makes it safe for a Rails application to expose MCP (Model Context Protocol)
|
|
@@ -27,7 +27,57 @@ files:
|
|
|
27
27
|
- CHANGELOG.md
|
|
28
28
|
- LICENSE.txt
|
|
29
29
|
- README.md
|
|
30
|
+
- lib/generators/reeve/install/install_generator.rb
|
|
31
|
+
- lib/generators/reeve/install/templates/create_audit_entries.rb.tt
|
|
32
|
+
- lib/generators/reeve/install/templates/initializer.rb.tt
|
|
30
33
|
- lib/reeve.rb
|
|
34
|
+
- lib/reeve/audit.rb
|
|
35
|
+
- lib/reeve/audit/entry.rb
|
|
36
|
+
- lib/reeve/audit/query.rb
|
|
37
|
+
- lib/reeve/audit/recorder.rb
|
|
38
|
+
- lib/reeve/audit/redactor.rb
|
|
39
|
+
- lib/reeve/authorization.rb
|
|
40
|
+
- lib/reeve/authorization/adapter.rb
|
|
41
|
+
- lib/reeve/authorization/adapters/plain.rb
|
|
42
|
+
- lib/reeve/authorization/adapters/pundit.rb
|
|
43
|
+
- lib/reeve/authorization/authorizer.rb
|
|
44
|
+
- lib/reeve/authorization/current.rb
|
|
45
|
+
- lib/reeve/authorization/declaration.rb
|
|
46
|
+
- lib/reeve/authorization/guard.rb
|
|
47
|
+
- lib/reeve/authorization/registry.rb
|
|
48
|
+
- lib/reeve/authorization/scoper.rb
|
|
49
|
+
- lib/reeve/configuration.rb
|
|
50
|
+
- lib/reeve/context.rb
|
|
51
|
+
- lib/reeve/decision.rb
|
|
52
|
+
- lib/reeve/errors.rb
|
|
53
|
+
- lib/reeve/fast_mcp.rb
|
|
54
|
+
- lib/reeve/integrations/fast_mcp/context_builder.rb
|
|
55
|
+
- lib/reeve/integrations/fast_mcp/tool_extension.rb
|
|
56
|
+
- lib/reeve/invocation.rb
|
|
57
|
+
- lib/reeve/minitest.rb
|
|
58
|
+
- lib/reeve/rspec.rb
|
|
59
|
+
- lib/reeve/scope_result.rb
|
|
60
|
+
- lib/reeve/testing.rb
|
|
61
|
+
- lib/reeve/testing/assertions.rb
|
|
62
|
+
- lib/reeve/testing/checks.rb
|
|
63
|
+
- lib/reeve/testing/checks/audit_coverage.rb
|
|
64
|
+
- lib/reeve/testing/checks/base.rb
|
|
65
|
+
- lib/reeve/testing/checks/contract_version.rb
|
|
66
|
+
- lib/reeve/testing/checks/cross_principal_leak.rb
|
|
67
|
+
- lib/reeve/testing/checks/guard_declared.rb
|
|
68
|
+
- lib/reeve/testing/checks/principal_required.rb
|
|
69
|
+
- lib/reeve/testing/checks/redaction_holds.rb
|
|
70
|
+
- lib/reeve/testing/checks/rule_present.rb
|
|
71
|
+
- lib/reeve/testing/compliance_assertions.rb
|
|
72
|
+
- lib/reeve/testing/compliance_suite.rb
|
|
73
|
+
- lib/reeve/testing/ledger.rb
|
|
74
|
+
- lib/reeve/testing/matchers.rb
|
|
75
|
+
- lib/reeve/testing/matchers/audit_every_call.rb
|
|
76
|
+
- lib/reeve/testing/matchers/base.rb
|
|
77
|
+
- lib/reeve/testing/matchers/deny_access_for.rb
|
|
78
|
+
- lib/reeve/testing/matchers/pass_reeve_check.rb
|
|
79
|
+
- lib/reeve/testing/report.rb
|
|
80
|
+
- lib/reeve/testing/result.rb
|
|
31
81
|
- lib/reeve/version.rb
|
|
32
82
|
homepage: https://github.com/vicmaster/reeve
|
|
33
83
|
licenses:
|
|
@@ -37,6 +87,7 @@ metadata:
|
|
|
37
87
|
source_code_uri: https://github.com/vicmaster/reeve
|
|
38
88
|
changelog_uri: https://github.com/vicmaster/reeve/blob/main/CHANGELOG.md
|
|
39
89
|
bug_tracker_uri: https://github.com/vicmaster/reeve/issues
|
|
90
|
+
rubygems_mfa_required: 'true'
|
|
40
91
|
post_install_message:
|
|
41
92
|
rdoc_options: []
|
|
42
93
|
require_paths:
|