olyx-guardrails 1.1.3 → 1.2.1
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 +29 -7
- data/README.md +109 -177
- data/lib/olyx/guardrails/check_result_builder.rb +12 -29
- data/lib/olyx/guardrails/check_runner.rb +11 -5
- data/lib/olyx/guardrails/check_set.rb +66 -19
- data/lib/olyx/guardrails/injection_patterns.rb +1 -2
- data/lib/olyx/guardrails/llm_analysis.rb +44 -14
- data/lib/olyx/guardrails/message_content.rb +17 -4
- data/lib/olyx/guardrails/pii/block_scrubber.rb +2 -2
- data/lib/olyx/guardrails/pii/message_scrubber.rb +2 -2
- data/lib/olyx/guardrails/pii/numeric_date_validator.rb +10 -5
- data/lib/olyx/guardrails/pii/text_scrubber.rb +15 -1
- data/lib/olyx/guardrails/policy/match_collector.rb +7 -1
- data/lib/olyx/guardrails/policy/rule_matcher.rb +26 -9
- data/lib/olyx/guardrails/policy.rb +37 -24
- data/lib/olyx/guardrails/policy_rule.rb +56 -19
- data/lib/olyx/guardrails/rails.rb +8 -7
- data/lib/olyx/guardrails/redaction/content_result.rb +4 -3
- data/lib/olyx/guardrails/risk/deterministic_score.rb +2 -4
- data/lib/olyx/guardrails/risk/weights.rb +0 -1
- data/lib/olyx/guardrails/risk_scorer.rb +4 -5
- data/lib/olyx/guardrails/secret_finding_collector.rb +0 -1
- data/lib/olyx/guardrails/secret_scanner.rb +1 -2
- data/lib/olyx/guardrails/secrets/pattern_catalog.rb +0 -10
- data/lib/olyx/guardrails/secrets/redactor.rb +0 -7
- data/lib/olyx/guardrails/secrets/source_set.rb +1 -2
- data/lib/olyx/guardrails/text/detection_variants.rb +13 -1
- data/lib/olyx/guardrails/version.rb +1 -1
- data/lib/olyx/guardrails.rb +1 -3
- metadata +2 -30
- data/lib/olyx/guardrails/check_pipeline.rb +0 -17
- data/lib/olyx/guardrails/checks/injection_check.rb +0 -20
- data/lib/olyx/guardrails/checks/injection_result.rb +0 -22
- data/lib/olyx/guardrails/checks/length_check.rb +0 -18
- data/lib/olyx/guardrails/checks/message_injection_check.rb +0 -19
- data/lib/olyx/guardrails/checks/pii_check.rb +0 -19
- data/lib/olyx/guardrails/checks/policy_check.rb +0 -26
- data/lib/olyx/guardrails/checks/secret_check.rb +0 -30
- data/lib/olyx/guardrails/checks/skipped_checks.rb +0 -26
- data/lib/olyx/guardrails/llm/analysis_normalizer.rb +0 -26
- data/lib/olyx/guardrails/llm/analysis_pipeline.rb +0 -37
- data/lib/olyx/guardrails/llm/boolean_validator.rb +0 -18
- data/lib/olyx/guardrails/llm/result_sanitizer.rb +0 -29
- data/lib/olyx/guardrails/message_check_runner.rb +0 -24
- data/lib/olyx/guardrails/message_check_set.rb +0 -20
- data/lib/olyx/guardrails/pii/ambiguous_numeric_date.rb +0 -32
- data/lib/olyx/guardrails/pii/hash_key.rb +0 -19
- data/lib/olyx/guardrails/pii/year_first_date.rb +0 -18
- data/lib/olyx/guardrails/policy/configuration.rb +0 -68
- data/lib/olyx/guardrails/policy/normalized_pattern_matcher.rb +0 -34
- data/lib/olyx/guardrails/policy/pattern_matcher.rb +0 -19
- data/lib/olyx/guardrails/policy_rule/configuration.rb +0 -52
- data/lib/olyx/guardrails/policy_rule/description_value.rb +0 -24
- data/lib/olyx/guardrails/policy_rule/name_value.rb +0 -22
- data/lib/olyx/guardrails/policy_rule/replacement_value.rb +0 -26
- data/lib/olyx/guardrails/policy_rule/text_values.rb +0 -23
- data/lib/olyx/guardrails/policy_rule/values.rb +0 -33
- data/lib/olyx/guardrails/secrets/confidentiality_source.rb +0 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 196f3e181407760bea2e26500d3e3eaa5ce29b886922ad361eab20e98ea76b68
|
|
4
|
+
data.tar.gz: 24144315dc438469cc42ca4d876834b44c3ad3b1b42430a80d6bc693e57ceec3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e3ecc41c16b5f43dc99beb1af720a1f5f0a4f68fbb5453504a21e01c14ff7f1c248e724bd7987e5e4db50aa15944cf03291556a41c951282b98351eb863e1512
|
|
7
|
+
data.tar.gz: 8814874a0d9f40c93a6deb3b2063da29378f6a149742319f19b9b6aa7a50142e52937baab9cc71e6ae58706d42a65820467391d8a583eae021f7d1cad7665b1f
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,31 @@
|
|
|
3
3
|
All notable changes to olyx-guardrails are documented here.
|
|
4
4
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [1.2.1] - 2026-11-11
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Simplify evaluation, message handling, LLM analysis, and numeric date validation
|
|
13
|
+
while preserving the public result contract.
|
|
14
|
+
- Require patched Rails test bundles: 8.1.4.1 or newer on Rails 8.1 and
|
|
15
|
+
8.0.5.2 or newer on Rails 8.0, with Rack >= 3.2.8 and Crass >= 1.0.8.
|
|
16
|
+
- Document request-body limits, host allowlists, and trusted proxy headers for
|
|
17
|
+
applications consuming the gem.
|
|
18
|
+
|
|
19
|
+
## [1.2.0] - 2026-10-02
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Scans long inputs in overlapping windows and narrows injection matching to
|
|
24
|
+
reduce missed detections and false positives.
|
|
25
|
+
- Sanitizes provider failures and stops treating confidentiality labels as
|
|
26
|
+
credentials; applications can enforce those labels with policy rules.
|
|
27
|
+
- Reports risk independently from blocking policy.
|
|
28
|
+
- Consolidates scanning and configuration internals, with tests,
|
|
29
|
+
documentation, and RuboCop as the public quality gate.
|
|
30
|
+
|
|
6
31
|
## [1.1.3] - 2026-08-07
|
|
7
32
|
|
|
8
33
|
### Changed
|
|
@@ -39,7 +64,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
39
64
|
decision has zero risk.
|
|
40
65
|
- The Rails generator provides copyable policy customization instructions and
|
|
41
66
|
tests the customized YAML path.
|
|
42
|
-
- Contributor setup,
|
|
67
|
+
- Contributor setup, local validation, and maintainer release
|
|
43
68
|
verification have canonical commands and documentation.
|
|
44
69
|
- Closely coupled proxy objects were folded into their owning runtime,
|
|
45
70
|
configuration, result-building, and notification components.
|
|
@@ -47,7 +72,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
47
72
|
ignored instead of being coerced across the untrusted provider boundary.
|
|
48
73
|
- Contributor and Rails appraisal locks use the current `net-imap` patch.
|
|
49
74
|
- CI refreshes the Ruby advisory database and blocks known-vulnerable locked
|
|
50
|
-
dependencies without making
|
|
75
|
+
dependencies without making offline local validation network-dependent.
|
|
51
76
|
|
|
52
77
|
## [1.0.0] - 2026-07-21
|
|
53
78
|
|
|
@@ -155,11 +180,8 @@ Initial public release.
|
|
|
155
180
|
|
|
156
181
|
- The core has one lightweight runtime dependency, Ruby's `base64` bundled gem.
|
|
157
182
|
Rails remains optional and is loaded only when used.
|
|
158
|
-
- CI enforces RuboCop
|
|
159
|
-
|
|
160
|
-
`ClassLength`) in place of a separate complexity tool — and a RubyCritic
|
|
161
|
-
maintainability gate, alongside the Appraisal matrix across Rails 8.0
|
|
162
|
-
and 8.1.
|
|
183
|
+
- CI enforces tests, documentation checks, RuboCop, and the Appraisal matrix
|
|
184
|
+
across Rails 8.0 and 8.1.
|
|
163
185
|
- Native RDoc covers every supported public class, module, constant, attribute,
|
|
164
186
|
and method. CI blocks undocumented additions to the explicit public API
|
|
165
187
|
manifest while leaving implementation-only constants outside the
|
data/README.md
CHANGED
|
@@ -1,65 +1,48 @@
|
|
|
1
1
|
# Olyx Guardrails
|
|
2
2
|
|
|
3
|
-
[](https://rubygems.org/gems/olyx-guardrails)
|
|
4
|
+
[](https://github.com/Olyx-labs/olyx-guardrails/releases/latest)
|
|
4
5
|
[](https://github.com/Olyx-labs/olyx-guardrails/actions/workflows/test.yml)
|
|
5
6
|
[](https://securityscorecards.dev/viewer/?uri=github.com/Olyx-labs/olyx-guardrails)
|
|
6
7
|
[](LICENSE)
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
applications that send or receive AI-generated content. It provides:
|
|
9
|
+
Input and output checks for AI applications in Ruby and Rails.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- completed-input and completed-output decisions;
|
|
15
|
-
- opt-in Rails adapters for common ingestion paths;
|
|
16
|
-
- provider-agnostic semantic analysis through a callable LLM hook; and
|
|
17
|
-
- sanitized notifications and Active Support instrumentation.
|
|
11
|
+
Detect prompt injection, PII, secrets, and application-specific restricted
|
|
12
|
+
content. Return explicit allow/block decisions and redact recognized content
|
|
13
|
+
at the boundaries your application chooses.
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
Olyx Guardrails is a standalone gem. It works without Olyx CLI, Olyx Cloud,
|
|
16
|
+
or an Olyx account. Deterministic checks run locally. An optional
|
|
17
|
+
`llm_provider` receives raw input and may send it elsewhere through your
|
|
18
|
+
application's adapter.
|
|
21
19
|
|
|
22
|
-
##
|
|
20
|
+
## Install
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
| Ruby | 3.4 or newer |
|
|
27
|
-
| Rails | 8.0 and 8.1 |
|
|
22
|
+
Requires **Ruby 3.4 or newer**. Rails is optional; the integration supports
|
|
23
|
+
**Rails 8.0 and 8.1**. The framework-free API does not load Rails.
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
The 1.1 release supports Rails 8.0 and 8.1. Only the listed Rails
|
|
32
|
-
series are tested and supported. When a series reaches upstream end-of-life,
|
|
33
|
-
a subsequent gem release may remove it instead of maintaining framework
|
|
34
|
-
security fixes independently. Every support change is recorded in the
|
|
35
|
-
changelog.
|
|
36
|
-
|
|
37
|
-
## Installation
|
|
38
|
-
|
|
39
|
-
Add the gem to the application:
|
|
25
|
+
Add to your `Gemfile`:
|
|
40
26
|
|
|
41
27
|
```ruby
|
|
42
|
-
gem "olyx-guardrails", "~> 1.
|
|
28
|
+
gem "olyx-guardrails", "~> 1.2"
|
|
43
29
|
```
|
|
44
30
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
```bash
|
|
31
|
+
```sh
|
|
48
32
|
bundle install
|
|
49
33
|
```
|
|
50
34
|
|
|
51
|
-
The only runtime gem dependency is Ruby's `base64`
|
|
52
|
-
bounded encoded-input detection.
|
|
35
|
+
The only runtime gem dependency is Ruby's bundled `base64` gem.
|
|
53
36
|
|
|
54
|
-
##
|
|
37
|
+
## Try it
|
|
55
38
|
|
|
56
|
-
|
|
39
|
+
Save this as `guardrails_example.rb`:
|
|
57
40
|
|
|
58
41
|
```ruby
|
|
59
42
|
require "olyx/guardrails"
|
|
60
43
|
|
|
61
44
|
policy = Olyx::Guardrails::Policy.new(
|
|
62
|
-
name: "
|
|
45
|
+
name: "application-boundary",
|
|
63
46
|
max_input_length: 4_000,
|
|
64
47
|
block_pii: false,
|
|
65
48
|
block_injections: true,
|
|
@@ -75,198 +58,147 @@ policy = Olyx::Guardrails::Policy.new(
|
|
|
75
58
|
]
|
|
76
59
|
)
|
|
77
60
|
|
|
61
|
+
input = "Contact ada@example.com about the release."
|
|
78
62
|
decision = Olyx::Guardrails.check(input, policy: policy)
|
|
79
|
-
|
|
63
|
+
abort "Input rejected" unless decision[:allowed]
|
|
80
64
|
|
|
81
|
-
|
|
82
|
-
|
|
65
|
+
puts "Allowed: #{decision[:allowed]}"
|
|
66
|
+
puts Olyx::Guardrails.redact(input, policy: policy)[:text]
|
|
83
67
|
|
|
84
|
-
|
|
85
|
-
|
|
68
|
+
restricted = Olyx::Guardrails.check("Discuss Project Falcon", policy: policy)
|
|
69
|
+
puts "Restricted input allowed: #{restricted[:allowed]}"
|
|
86
70
|
```
|
|
87
71
|
|
|
88
|
-
|
|
89
|
-
recognized content and never makes an allow/block decision. Applications that
|
|
90
|
-
need both operations call both explicitly.
|
|
72
|
+
Run it:
|
|
91
73
|
|
|
92
|
-
|
|
74
|
+
```sh
|
|
75
|
+
bundle exec ruby guardrails_example.rb
|
|
76
|
+
```
|
|
93
77
|
|
|
94
|
-
|
|
95
|
-
- blocks detected injection attempts;
|
|
96
|
-
- reports, but does not block, detected PII and secrets; and
|
|
97
|
-
- has no custom restricted-content rules.
|
|
78
|
+
Expected output:
|
|
98
79
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
[
|
|
102
|
-
|
|
80
|
+
```text
|
|
81
|
+
Allowed: true
|
|
82
|
+
Contact [EMAIL] about the release.
|
|
83
|
+
Restricted input allowed: false
|
|
84
|
+
```
|
|
103
85
|
|
|
104
|
-
|
|
86
|
+
In this policy, PII is reported without blocking, then explicitly redacted.
|
|
87
|
+
Injection attempts, secrets, and the configured project name cause rejection.
|
|
88
|
+
Policies are immutable: construct them once and reuse them.
|
|
89
|
+
|
|
90
|
+
**Checking and redacting are separate operations.** `check` returns a decision
|
|
91
|
+
without changing input. `redact` transforms recognized content without making
|
|
92
|
+
an allow/block decision. Redaction does not override a rejection.
|
|
93
|
+
|
|
94
|
+
Use `check_messages` for structured conversations and adjacent-turn detection.
|
|
95
|
+
Use `check_output` and `redact_output` at completed-output boundaries. Your
|
|
96
|
+
application owns the model call and must check the completed response before
|
|
97
|
+
returning it. See the [complete Ruby example](https://github.com/Olyx-labs/olyx-guardrails/blob/master/examples/ruby_only.rb) and
|
|
98
|
+
[API reference](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/API.md) for the full flow and result contracts.
|
|
99
|
+
|
|
100
|
+
## Add Rails enforcement
|
|
105
101
|
|
|
106
102
|
Generate an initializer and environment-keyed policy file:
|
|
107
103
|
|
|
108
|
-
```
|
|
104
|
+
```sh
|
|
109
105
|
bin/rails generate olyx_guardrails:install
|
|
110
106
|
```
|
|
111
107
|
|
|
112
|
-
|
|
108
|
+
Review `config/initializers/olyx_guardrails.rb` and
|
|
109
|
+
`config/olyx_guardrails.yml`, then include an adapter at an application boundary:
|
|
113
110
|
|
|
114
111
|
```ruby
|
|
115
112
|
class AiRequestsController < ApplicationController
|
|
116
113
|
include Olyx::Guardrails::Rails::Controller
|
|
117
114
|
|
|
118
115
|
rescue_from Olyx::Guardrails::Blocked do |error|
|
|
119
|
-
render json: {
|
|
120
|
-
|
|
121
|
-
decision: error.decision
|
|
122
|
-
}, status: :unprocessable_entity
|
|
116
|
+
render json: { error: "input_rejected", decision: error.decision },
|
|
117
|
+
status: :unprocessable_entity
|
|
123
118
|
end
|
|
124
119
|
|
|
125
120
|
def create
|
|
126
121
|
prompt = params.require(:prompt)
|
|
127
|
-
guardrails_check!(prompt
|
|
128
|
-
|
|
122
|
+
guardrails_check!(prompt)
|
|
129
123
|
safe_prompt = guardrails_redact(prompt)[:text]
|
|
124
|
+
|
|
130
125
|
completion = LlmClient.complete(safe_prompt)
|
|
131
126
|
Olyx::Guardrails::Rails::Enforcer.check_output!(completion)
|
|
127
|
+
safe_output = Olyx::Guardrails::Rails.redact_output(completion)[:text]
|
|
132
128
|
|
|
133
|
-
render json: { completion:
|
|
129
|
+
render json: { completion: safe_output }
|
|
134
130
|
end
|
|
135
131
|
end
|
|
136
132
|
```
|
|
137
133
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
The
|
|
142
|
-
[Rails integration guide](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/RAILS.md)
|
|
143
|
-
covers every adapter, boot-time configuration, safe YAML loading,
|
|
144
|
-
instrumentation payloads, and notification delivery.
|
|
134
|
+
`LlmClient` represents your application's existing model client. This controller
|
|
135
|
+
example belongs in a Rails application; it is not a standalone script.
|
|
145
136
|
|
|
146
|
-
|
|
137
|
+
Enforcement is opt-in. The gem does not automatically scan every request, job,
|
|
138
|
+
upload, or model call. The [Rails guide](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/RAILS.md) covers controllers,
|
|
139
|
+
Active Job, Active Model, GraphQL, Action Cable, extracted upload text,
|
|
140
|
+
instrumentation, and notifications.
|
|
147
141
|
|
|
148
|
-
|
|
149
|
-
|---|---|---|
|
|
150
|
-
| Plain text input | `Guardrails.check` | Returns an allow/block decision |
|
|
151
|
-
| Structured messages | `Guardrails.check_messages` | Adds adjacent-turn detection |
|
|
152
|
-
| Completed model output | `Guardrails.check_output` | Explicit output decision |
|
|
153
|
-
| Plain text transformation | `Guardrails.redact` | Returns redacted text |
|
|
154
|
-
| Completed output transformation | `Guardrails.redact_output` | Explicit output transformation |
|
|
155
|
-
| Rails exception flow | `Rails::Enforcer.check!` | Raises `Guardrails::Blocked` when rejected |
|
|
156
|
-
| Low-level secret enforcement | `SecretScanner.scan!` | Raises `SecretScanner::Blocked` on a finding |
|
|
142
|
+
## Defaults and operational behavior
|
|
157
143
|
|
|
158
|
-
|
|
159
|
-
token stream before content reaches the caller.
|
|
160
|
-
|
|
161
|
-
## Optional LLM provider
|
|
162
|
-
|
|
163
|
-
`llm_provider` accepts any callable object. It receives the raw text and a
|
|
164
|
-
bounded context hash containing deterministic findings:
|
|
144
|
+
The default policy:
|
|
165
145
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
146
|
+
- rejects input longer than 10,000 Ruby characters;
|
|
147
|
+
- blocks detected prompt injection;
|
|
148
|
+
- reports PII and secrets without blocking them;
|
|
149
|
+
- defines no custom restricted-content rules; and
|
|
150
|
+
- preserves the deterministic decision if an optional LLM provider fails.
|
|
170
151
|
|
|
171
|
-
|
|
152
|
+
Define an explicit production policy. If semantic analysis is required, choose
|
|
153
|
+
`llm_failure_mode: :block` or `:raise`. The application supplies the classifier
|
|
154
|
+
callable and owns its authentication, transport, timeouts, and retries.
|
|
155
|
+
Semantic findings can add violations but cannot clear deterministic findings.
|
|
156
|
+
See [policies](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/POLICIES.md) and the
|
|
157
|
+
[provider contract](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/API.md#llm-provider-contract).
|
|
172
158
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
llm_provider: provider
|
|
177
|
-
)
|
|
178
|
-
```
|
|
159
|
+
Rails can publish content-free Active Support events. Optional notifier handlers
|
|
160
|
+
receive sanitized events and run synchronously unless your application queues
|
|
161
|
+
delivery. See [operations](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/OPERATIONS.md) for data handling and failure modes.
|
|
179
162
|
|
|
180
|
-
|
|
163
|
+
## Limitations
|
|
181
164
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
risk_score: 0.2,
|
|
188
|
-
reason: "No semantic violation found"
|
|
189
|
-
}
|
|
190
|
-
```
|
|
165
|
+
Pattern-based checks can miss novel attacks, paraphrasing, translations,
|
|
166
|
+
unsupported formats, and nested encodings. PII coverage is strongest for the
|
|
167
|
+
documented North American and common international formats. Secret detection
|
|
168
|
+
is format-based. Restricted-content rules do not infer semantic equivalents
|
|
169
|
+
unless your application supplies additional analysis.
|
|
191
170
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
171
|
+
Completed-output methods evaluate complete values; they do not enforce a policy
|
|
172
|
+
on tokens already streamed to a caller. Model routing, tool permissions,
|
|
173
|
+
authorization, file extraction, distributed quotas, and audit storage remain
|
|
174
|
+
application responsibilities.
|
|
195
175
|
|
|
196
|
-
|
|
197
|
-
[
|
|
198
|
-
|
|
199
|
-
[local HTTP example](https://github.com/Olyx-labs/olyx-guardrails/blob/master/examples/local_llm_provider.rb).
|
|
200
|
-
The example works with an application-owned classifier sidecar backed by any
|
|
201
|
-
inference runtime. Read the
|
|
202
|
-
[model-suitability criteria](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/OPERATIONS.md#model-suitability)
|
|
203
|
-
before using semantic analysis as a blocking production control.
|
|
176
|
+
Treat guardrails as one layer of application protection. Read the
|
|
177
|
+
[detection limits](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/OPERATIONS.md#detection-limits) before enabling production
|
|
178
|
+
blocking.
|
|
204
179
|
|
|
205
|
-
## Documentation
|
|
180
|
+
## Documentation and examples
|
|
206
181
|
|
|
207
182
|
- [Documentation index](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/README.md)
|
|
208
183
|
- [Policies and restricted content](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/POLICIES.md)
|
|
209
184
|
- [Rails integration](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/RAILS.md)
|
|
210
|
-
- [Operations and
|
|
211
|
-
- [API reference](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/API.md)
|
|
212
|
-
- [
|
|
213
|
-
- [
|
|
214
|
-
- [
|
|
215
|
-
- [Changelog](CHANGELOG.md)
|
|
216
|
-
|
|
217
|
-
Examples:
|
|
218
|
-
|
|
219
|
-
- [Framework-free Ruby](https://github.com/Olyx-labs/olyx-guardrails/blob/master/examples/ruby_only.rb)
|
|
220
|
-
- [Custom policy](https://github.com/Olyx-labs/olyx-guardrails/blob/master/examples/custom_policy.rb)
|
|
221
|
-
- [Rails opt-in boundaries](https://github.com/Olyx-labs/olyx-guardrails/blob/master/examples/rails_opt_in.rb)
|
|
222
|
-
- [Local LLM provider](https://github.com/Olyx-labs/olyx-guardrails/blob/master/examples/local_llm_provider.rb)
|
|
185
|
+
- [Operations and notifications](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/OPERATIONS.md)
|
|
186
|
+
- [Ruby API reference](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/API.md)
|
|
187
|
+
- [Runnable Ruby example](https://github.com/Olyx-labs/olyx-guardrails/blob/master/examples/ruby_only.rb)
|
|
188
|
+
- [Custom policies](https://github.com/Olyx-labs/olyx-guardrails/blob/master/examples/custom_policy.rb)
|
|
189
|
+
- [Application-owned local classifier](https://github.com/Olyx-labs/olyx-guardrails/blob/master/examples/local_llm_provider.rb)
|
|
223
190
|
- [Notifier handlers](https://github.com/Olyx-labs/olyx-guardrails/blob/master/examples/notifier.rb)
|
|
224
191
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
boundary or data-loss-prevention system.
|
|
233
|
-
|
|
234
|
-
- Pattern-based detection can miss paraphrasing, translations, novel attacks,
|
|
235
|
-
unsupported homoglyphs, and nested encoding beyond the bounded normalization
|
|
236
|
-
pass.
|
|
237
|
-
- PII support is strongest for documented North American and common
|
|
238
|
-
international formats.
|
|
239
|
-
- Secret detection is format-based and cannot classify every high-entropy
|
|
240
|
-
string or future credential format.
|
|
241
|
-
- Restricted-content rules are deterministic unless an application supplies an
|
|
242
|
-
LLM provider.
|
|
243
|
-
- File parsing, authorization, streaming enforcement, distributed quotas,
|
|
244
|
-
centralized policy rollout, and audit retention remain application or
|
|
245
|
-
platform responsibilities.
|
|
246
|
-
|
|
247
|
-
The
|
|
248
|
-
[operations guide](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/OPERATIONS.md)
|
|
249
|
-
describes failure modes, data handling, risk scores, concurrency, and
|
|
250
|
-
deployment boundaries. Report security issues through the
|
|
251
|
-
[security policy](https://github.com/Olyx-labs/olyx-guardrails/security/policy),
|
|
252
|
-
not the public issue tracker.
|
|
253
|
-
|
|
254
|
-
## Development
|
|
255
|
-
|
|
256
|
-
Run the same gates used by CI:
|
|
257
|
-
|
|
258
|
-
```bash
|
|
259
|
-
bin/setup
|
|
260
|
-
bin/ci
|
|
261
|
-
```
|
|
192
|
+
## Contributing and security
|
|
193
|
+
|
|
194
|
+
See [CONTRIBUTING.md](https://github.com/Olyx-labs/olyx-guardrails/blob/master/CONTRIBUTING.md) for development setup and checks,
|
|
195
|
+
[CHANGELOG.md](CHANGELOG.md) for release changes, and the
|
|
196
|
+
[release runbook](https://github.com/Olyx-labs/olyx-guardrails/blob/master/docs/RELEASING.md) for maintainer procedures.
|
|
197
|
+
|
|
198
|
+
Rails adapter changes must also pass `bundle exec appraisal rake test`.
|
|
262
199
|
|
|
263
|
-
|
|
264
|
-
from the default test suite; Rails remains an optional runtime dependency for
|
|
265
|
-
gem consumers. The Rails compatibility matrix is managed with Appraisal. See
|
|
266
|
-
[contribution guide](https://github.com/Olyx-labs/olyx-guardrails/blob/master/CONTRIBUTING.md)
|
|
267
|
-
before submitting a change.
|
|
200
|
+
Report vulnerabilities through the [security policy](https://github.com/Olyx-labs/olyx-guardrails/blob/master/SECURITY.md).
|
|
268
201
|
|
|
269
202
|
## License
|
|
270
203
|
|
|
271
|
-
Olyx Guardrails is available under the
|
|
272
204
|
[Apache License 2.0](LICENSE).
|
|
@@ -10,42 +10,25 @@ module Olyx
|
|
|
10
10
|
|
|
11
11
|
module_function
|
|
12
12
|
|
|
13
|
+
# Keep the fixed public contract together rather than splitting its fields across helpers.
|
|
14
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
13
15
|
def call(checks, analysis, policy)
|
|
14
16
|
ordered = CHECK_ORDER.filter_map { |type| checks[type] }
|
|
15
|
-
|
|
16
|
-
.merge(risk(checks, ordered, analysis))
|
|
17
|
-
.merge(analysis_result(analysis))
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def base_result(checks, ordered, policy)
|
|
21
|
-
detection(checks, ordered).merge(policy(checks[:policy], policy))
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def detection(checks, ordered)
|
|
25
|
-
{
|
|
17
|
+
result = {
|
|
26
18
|
allowed: ordered.all? { |check| check[:allowed] },
|
|
27
19
|
pii_detected: checks[:pii][:detected],
|
|
28
20
|
injection_attempt: checks[:injection][:injection_attempt],
|
|
29
|
-
secret_leaked: checks[:secret][:leaked]
|
|
21
|
+
secret_leaked: checks[:secret][:leaked],
|
|
22
|
+
policy_name: policy.name,
|
|
23
|
+
policy_violated: checks[:policy][:violated],
|
|
24
|
+
policy_findings: checks[:policy][:findings],
|
|
25
|
+
risk_score: RiskScorer.call(checks, analysis),
|
|
26
|
+
checks: ordered
|
|
30
27
|
}
|
|
28
|
+
result[:llm_analysis] = analysis if analysis
|
|
29
|
+
result
|
|
31
30
|
end
|
|
32
|
-
|
|
33
|
-
def policy(check, configuration)
|
|
34
|
-
{
|
|
35
|
-
policy_name: configuration.name,
|
|
36
|
-
policy_violated: check[:violated],
|
|
37
|
-
policy_findings: check[:findings]
|
|
38
|
-
}
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def risk(checks, ordered, analysis)
|
|
42
|
-
{ risk_score: RiskScorer.call(checks, ordered, analysis), checks: ordered }
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def analysis_result(analysis)
|
|
46
|
-
analysis ? { llm_analysis: analysis } : {}
|
|
47
|
-
end
|
|
48
|
-
private_class_method :base_result, :detection, :policy, :risk, :analysis_result
|
|
31
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
49
32
|
end
|
|
50
33
|
end
|
|
51
34
|
end
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
require_relative 'check_result_builder'
|
|
4
4
|
require_relative 'check_analyzer'
|
|
5
5
|
require_relative 'check_set'
|
|
6
|
-
require_relative 'check_pipeline'
|
|
7
6
|
require_relative 'policy'
|
|
8
7
|
require_relative 'validation'
|
|
8
|
+
require_relative 'message_source'
|
|
9
9
|
|
|
10
10
|
module Olyx
|
|
11
11
|
module Guardrails
|
|
@@ -15,16 +15,22 @@ module Olyx
|
|
|
15
15
|
new(input, **).call
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def initialize(input, policy:, llm_provider:)
|
|
19
|
-
|
|
18
|
+
def initialize(input, policy:, llm_provider:, structured: false)
|
|
19
|
+
Validation.array_of!(input, Hash, name: 'messages') if structured
|
|
20
|
+
@messages = input if structured
|
|
21
|
+
@source = input.to_s unless structured
|
|
20
22
|
@policy = policy
|
|
21
23
|
@llm_provider = llm_provider
|
|
22
24
|
validate_options!
|
|
25
|
+
@source = MessageSource.call(input) if structured
|
|
23
26
|
end
|
|
24
27
|
|
|
25
28
|
def call
|
|
26
|
-
checks = CheckSet.call(@source, policy: @policy)
|
|
27
|
-
|
|
29
|
+
checks = CheckSet.call(@source, policy: @policy, messages: @messages)
|
|
30
|
+
merged, analysis = CheckAnalyzer.call(
|
|
31
|
+
checks, provider: @llm_provider, source: @source, policy: @policy
|
|
32
|
+
)
|
|
33
|
+
CheckResultBuilder.call(merged, analysis, @policy)
|
|
28
34
|
end
|
|
29
35
|
|
|
30
36
|
private
|
|
@@ -1,33 +1,80 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative '
|
|
4
|
-
require_relative '
|
|
5
|
-
require_relative '
|
|
6
|
-
require_relative '
|
|
7
|
-
require_relative 'checks/secret_check'
|
|
8
|
-
require_relative 'checks/skipped_checks'
|
|
3
|
+
require_relative 'injection_detector'
|
|
4
|
+
require_relative 'pii/text_scrubber'
|
|
5
|
+
require_relative 'policy_scanner'
|
|
6
|
+
require_relative 'secret_scanner'
|
|
9
7
|
|
|
10
8
|
module Olyx
|
|
11
9
|
module Guardrails
|
|
12
10
|
# Coordinates independent deterministic checks for one normalized input.
|
|
13
11
|
class CheckSet
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
secret: Checks::SecretCheck,
|
|
18
|
-
policy: Checks::PolicyCheck
|
|
19
|
-
}.freeze
|
|
20
|
-
|
|
21
|
-
def self.call(source, policy:)
|
|
22
|
-
length = Checks::LengthCheck.call(source, policy)
|
|
23
|
-
content = length[:allowed] ? scan(source, policy) : Checks::SkippedChecks.call
|
|
12
|
+
def self.call(source, policy:, messages: nil)
|
|
13
|
+
length = length_check(source, policy)
|
|
14
|
+
content = length[:allowed] ? scan(source, policy, messages) : skipped_checks
|
|
24
15
|
content.merge(length: length)
|
|
25
16
|
end
|
|
26
17
|
|
|
27
|
-
def self.scan(source, policy)
|
|
28
|
-
|
|
18
|
+
def self.scan(source, policy, messages)
|
|
19
|
+
{
|
|
20
|
+
pii: pii_check(source, policy),
|
|
21
|
+
injection: injection_check(source, policy, messages),
|
|
22
|
+
secret: secret_check(source, policy),
|
|
23
|
+
policy: policy_check(source, policy)
|
|
24
|
+
}
|
|
29
25
|
end
|
|
30
|
-
|
|
26
|
+
|
|
27
|
+
def self.length_check(source, policy)
|
|
28
|
+
length = source.length
|
|
29
|
+
maximum = policy.max_input_length
|
|
30
|
+
{ type: 'length', allowed: length <= maximum, length: length, max_length: maximum }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.pii_check(source, policy)
|
|
34
|
+
detected = Pii::TextScrubber.detect?(source)
|
|
35
|
+
{ type: 'pii', allowed: !detected || !policy.block_pii?, detected: detected }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.injection_check(source, policy, messages)
|
|
39
|
+
scan = InjectionDetector.scan(messages || [{ 'role' => 'user', 'content' => source }])
|
|
40
|
+
attempt = scan[:injection_attempt]
|
|
41
|
+
{
|
|
42
|
+
type: 'injection',
|
|
43
|
+
allowed: !attempt || !policy.block_injections?,
|
|
44
|
+
injection_attempt: attempt,
|
|
45
|
+
patterns: scan[:patterns]
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.secret_check(source, policy)
|
|
50
|
+
scan = SecretScanner.scan(source, custom_patterns: policy.secret_patterns)
|
|
51
|
+
leaked = scan[:leaked]
|
|
52
|
+
{ type: 'secret', allowed: !leaked || !policy.block_secrets?, leaked: leaked, count: scan[:findings].size }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.policy_check(source, policy)
|
|
56
|
+
scan = PolicyScanner.scan(source, policy: policy)
|
|
57
|
+
findings = scan[:findings]
|
|
58
|
+
{
|
|
59
|
+
type: 'policy', allowed: !scan[:blocked], violated: scan[:violated],
|
|
60
|
+
count: findings.size, findings: findings
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.skipped_checks
|
|
65
|
+
{
|
|
66
|
+
pii: skipped('pii', detected: false),
|
|
67
|
+
injection: skipped('injection', injection_attempt: false, patterns: []),
|
|
68
|
+
secret: skipped('secret', leaked: false, count: 0),
|
|
69
|
+
policy: skipped('policy', violated: false, count: 0, findings: [])
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.skipped(type, **fields)
|
|
74
|
+
{ type: type, allowed: true, skipped: true, **fields }
|
|
75
|
+
end
|
|
76
|
+
private_class_method :injection_check, :length_check, :pii_check, :policy_check, :scan,
|
|
77
|
+
:secret_check, :skipped, :skipped_checks
|
|
31
78
|
end
|
|
32
79
|
end
|
|
33
80
|
end
|
|
@@ -19,8 +19,7 @@ module Olyx
|
|
|
19
19
|
/forget\s+(?:all\s+)?(?:your|previous)\s+instructions?/i,
|
|
20
20
|
/your\s+(?:new\s+)?(?:instructions?\s+are|rules?\s+are)/i,
|
|
21
21
|
/(?:override|bypass|ignore)\s+your\s+(?:instructions?|safety|restrictions?|filters?|rules?|training)/i,
|
|
22
|
-
/
|
|
23
|
-
/you\s+are\s+now\s+(?:a|an|free|DAN|unrestricted)/i,
|
|
22
|
+
/you\s+are\s+now\s+(?:an?\s+)?(?:free|DAN|unrestricted)(?:\s+AI)?/i,
|
|
24
23
|
/(?:jailbreak|dan\s+mode|developer\s+mode|god\s+mode|unrestricted\s+mode)/i,
|
|
25
24
|
/do\s+anything\s+now/i,
|
|
26
25
|
/you\s+have\s+no\s+(?:restrictions?|limits?|rules?|filters?)/i,
|