fiber_audit 0.3.0 → 0.3.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/.fiber-audit.example.yml +7 -8
- data/ARCHITECTURE.md +92 -670
- data/CHANGELOG.md +29 -0
- data/README.md +145 -92
- data/lib/fiber_audit/cli.rb +3 -1
- data/lib/fiber_audit/configuration.rb +31 -6
- data/lib/fiber_audit/operation_semantics.rb +172 -0
- data/lib/fiber_audit/runtime/active_operations.rb +25 -9
- data/lib/fiber_audit/runtime/boot.rb +4 -0
- data/lib/fiber_audit/runtime/environment.rb +58 -0
- data/lib/fiber_audit/runtime/lifecycle.rb +91 -50
- data/lib/fiber_audit/runtime/operation_liveness_monitor.rb +282 -0
- data/lib/fiber_audit/runtime/operation_liveness_policy.rb +47 -0
- data/lib/fiber_audit/runtime/probes/base.rb +12 -10
- data/lib/fiber_audit/runtime/scheduler_evidence_classifier.rb +60 -0
- data/lib/fiber_audit/runtime/scheduler_snapshot.rb +6 -24
- data/lib/fiber_audit/runtime/watchdog.rb +17 -9
- data/lib/fiber_audit/runtime.rb +4 -0
- data/lib/fiber_audit/static/rules/blocking_subprocess.rb +3 -26
- data/lib/fiber_audit/static/rules/direct_socket.rb +52 -16
- data/lib/fiber_audit/static/rules/thread_current_state.rb +12 -20
- data/lib/fiber_audit/version.rb +1 -1
- metadata +5 -1
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.3.1 (2026-08-23)
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added a lifecycle-owned operation-liveness monitor with strict policy transport,
|
|
10
|
+
explicit active/disabled/unsupported states, and bounded long-active
|
|
11
|
+
start/completion evidence independent of scheduler-heartbeat stalls.
|
|
12
|
+
- Added shared operation semantics and five Boolean/nil scheduler-classification
|
|
13
|
+
measurements to targeted operations, watchdog overlaps, and long-active events.
|
|
14
|
+
- Expanded the bounded scheduler semantic matrix for Process wait variants,
|
|
15
|
+
coordination hooks, localhost address resolution, and Ruby 4 IO-close
|
|
16
|
+
interruption; added a local measurement-only runtime probe benchmark.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Scheduler snapshot failures now remain `nil`/unknown in JSONL schema 1.0 rather
|
|
21
|
+
than asserting false state.
|
|
22
|
+
- FA1006 keeps rule/operation identity while distinguishing socket allocation,
|
|
23
|
+
endpoint setup, local connection, and unknown subclass constructor semantics.
|
|
24
|
+
- FA1004 now reports medium severity without execution-context escalation and
|
|
25
|
+
describes possible shared-state exposure without claiming request leakage.
|
|
26
|
+
- Documented the tested support contract as CRuby 3.3, 3.4, and 4.0 on Ubuntu
|
|
27
|
+
Linux; the gem installation requirement remains Ruby 3.3 or newer.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Corrected scheduler snapshots to preserve the actual `Fiber#blocking?` Boolean
|
|
32
|
+
instead of converting both blocking and non-blocking Fibers to true.
|
|
33
|
+
|
|
5
34
|
## 0.3.0 (2026-08-12)
|
|
6
35
|
|
|
7
36
|
### Changed
|
data/README.md
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
# FiberAudit
|
|
2
2
|
|
|
3
3
|
FiberAudit audits Ruby and Rails code for operations that require cooperation
|
|
4
|
-
from a Fiber scheduler.
|
|
5
|
-
|
|
4
|
+
from a Fiber scheduler. It provides Rubydex/Prism-backed static hypotheses and
|
|
5
|
+
an explicitly activated observational runtime recorder.
|
|
6
6
|
|
|
7
7
|
> **Safety disclaimer:** FiberAudit does not prove that an application is
|
|
8
|
-
> fiber-safe.
|
|
9
|
-
>
|
|
8
|
+
> fiber-safe. Runtime observations prove execution, not scheduler harm; temporal
|
|
9
|
+
> overlap and long-active duration do not prove causality or deadlock. Absence of
|
|
10
|
+
> events does not establish execution coverage. FiberAudit never emits an
|
|
11
|
+
> unconditional `PASS`.
|
|
10
12
|
|
|
11
13
|
## Requirements and installation
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
The gem requires Ruby 3.3 or newer. CI tests CRuby 3.3, 3.4, and 4.0 on Ubuntu
|
|
16
|
+
Linux. Other Ruby engines and operating systems are not currently part of the
|
|
17
|
+
tested support contract. Native Rubydex packages must be available for the
|
|
18
|
+
selected platform.
|
|
14
19
|
|
|
15
20
|
```sh
|
|
16
21
|
gem install fiber_audit
|
|
17
22
|
```
|
|
18
23
|
|
|
19
|
-
Or add it to a bundle
|
|
24
|
+
Or add it to a bundle:
|
|
20
25
|
|
|
21
26
|
```ruby
|
|
22
27
|
gem "fiber_audit", require: false
|
|
@@ -24,15 +29,15 @@ gem "fiber_audit", require: false
|
|
|
24
29
|
|
|
25
30
|
## Quick start
|
|
26
31
|
|
|
27
|
-
Run from a project directory or
|
|
32
|
+
Run from a project directory or beneath it:
|
|
28
33
|
|
|
29
34
|
```sh
|
|
30
35
|
fiber-audit static
|
|
36
|
+
fiber-audit runtime -- bundle exec rspec
|
|
31
37
|
```
|
|
32
38
|
|
|
33
|
-
FiberAudit walks upward to
|
|
34
|
-
`config/application.rb
|
|
35
|
-
file exists.
|
|
39
|
+
FiberAudit walks upward to the nearest `Gemfile`, `gems.rb`, or
|
|
40
|
+
`config/application.rb` and loads `.fiber-audit.yml` from that root when present.
|
|
36
41
|
|
|
37
42
|
```text
|
|
38
43
|
fiber-audit static [--format text|json] [--config PATH] [--out PATH]
|
|
@@ -44,9 +49,9 @@ fiber-audit explain FA1001
|
|
|
44
49
|
fiber-audit version
|
|
45
50
|
```
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
JSON, writes only the report to that file, and prints a one-line
|
|
49
|
-
Explicit `--format` always wins.
|
|
52
|
+
Static output defaults to text on a TTY and JSON when piped. `--out PATH`
|
|
53
|
+
defaults to JSON, writes only the report to that file, and prints a one-line
|
|
54
|
+
confirmation. Explicit `--format` always wins.
|
|
50
55
|
|
|
51
56
|
## Shipped rules
|
|
52
57
|
|
|
@@ -55,75 +60,124 @@ Explicit `--format` always wins.
|
|
|
55
60
|
| FA1001 | Subprocess creation, replacement, waiting, and streams | info/medium |
|
|
56
61
|
| FA1002 | Thread-wait scheduler coordination | low |
|
|
57
62
|
| FA1003 | Synchronization scheduler coordination | low/info |
|
|
58
|
-
| FA1004 |
|
|
63
|
+
| FA1004 | Thread-variable access shared across Fibers on one Thread | medium |
|
|
59
64
|
| FA1005 | `IO.select` scheduler capability requirement | medium |
|
|
60
|
-
| FA1006 | Socket
|
|
65
|
+
| FA1006 | Socket allocation and constructor endpoint semantics | low |
|
|
61
66
|
| FA1007 | HTTP scheduler cooperation in request-like contexts | medium |
|
|
62
67
|
|
|
68
|
+
FA1004 reports true `thread_variable_get/set` access. It does not retain keys or
|
|
69
|
+
values and does not claim that request-sensitive leakage occurred. FA1006 keeps
|
|
70
|
+
stable `<Class>.new` operation identities while distinguishing inventory-only
|
|
71
|
+
allocation, address resolution/network endpoint setup, local connection, and
|
|
72
|
+
unknown IPSocket-subclass construction.
|
|
73
|
+
|
|
63
74
|
Use `fiber-audit explain <RULE_ID>` for exact targets and remediation.
|
|
64
75
|
|
|
65
76
|
## Runtime audit
|
|
66
77
|
|
|
67
|
-
The
|
|
68
|
-
never executes source fragments
|
|
78
|
+
The runtime command observes only the command supplied after `--`; static
|
|
79
|
+
analysis never executes discovered source fragments. Each observed Ruby process
|
|
80
|
+
writes a separate owner-only JSONL schema `1.0` session under
|
|
81
|
+
`tmp/fiber-audit-runtime` by default.
|
|
69
82
|
|
|
70
|
-
|
|
71
|
-
|
|
83
|
+
Targeted probes cover the operations represented by FA1001–FA1007. Events retain
|
|
84
|
+
canonical operation names, monotonic duration, conservative project-relative
|
|
85
|
+
callsites, execution context, ephemeral Thread/Fiber identities, and allowlisted
|
|
86
|
+
scalar measurements. They never retain commands, command arguments, URLs,
|
|
87
|
+
addresses, hosts, ports, headers, payloads, responses, return values, exception
|
|
88
|
+
messages, environment secrets, or thread-variable keys and values.
|
|
89
|
+
|
|
90
|
+
Libraries such as Open3, Monitor, Socket, Net::HTTP, and OpenURI may load after
|
|
91
|
+
runtime boot; FiberAudit rescans only known targets after `require`. Runtime
|
|
92
|
+
wrappers remain inert outside explicit activation and after deactivation or fork.
|
|
93
|
+
|
|
94
|
+
### Scheduler snapshots and operation classification
|
|
95
|
+
|
|
96
|
+
A targeted operation snapshots scheduler state at its start. Measurement failure
|
|
97
|
+
is represented as `nil` (unknown), never coerced to false:
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
scheduler_present: true | false | nil
|
|
101
|
+
fiber_blocking: true | false | nil
|
|
102
|
+
scheduler_io_select_supported: true | false | nil
|
|
103
|
+
scheduler_process_wait_supported: true | false | nil
|
|
104
|
+
scheduler_address_resolve_supported: true | false | nil
|
|
72
105
|
```
|
|
73
106
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
The scheduler watchdog records one bounded start/completion pair when its
|
|
98
|
-
scheduler-owned heartbeat stops progressing past the configured threshold.
|
|
99
|
-
Scheduler-friendly waits should continue heartbeats. A session also records an
|
|
100
|
-
explicit watchdog state:
|
|
107
|
+
Operation-specific evidence adds five Boolean/nil measurements:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
operation_wait_possible
|
|
111
|
+
operation_inventory_only
|
|
112
|
+
operation_scheduler_capability_required
|
|
113
|
+
operation_scheduler_capability_supported
|
|
114
|
+
operation_scheduler_cooperation_available
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`operation_scheduler_cooperation_available: true` means the captured scheduler
|
|
118
|
+
and Fiber mode were compatible and, for an optional capability, the captured
|
|
119
|
+
hook was supported. Required core coordination hooks such as `block` and
|
|
120
|
+
`kernel_sleep` are inferred from known scheduler presence rather than measured
|
|
121
|
+
separately. This does not prove that the operation cooperated or completed
|
|
122
|
+
without delay. `nil` remains unknown or not applicable.
|
|
123
|
+
|
|
124
|
+
### Scheduler watchdog
|
|
125
|
+
|
|
126
|
+
The watchdog records one bounded start/completion pair when a scheduler-owned
|
|
127
|
+
heartbeat stops progressing past its threshold. Scheduler-friendly waits should
|
|
128
|
+
continue heartbeats. State events are:
|
|
101
129
|
|
|
102
130
|
- `watchdog_active` — a heartbeat ran under an installed scheduler;
|
|
103
131
|
- `watchdog_absent` — no scheduler was observed;
|
|
104
|
-
- `watchdog_unsupported` —
|
|
132
|
+
- `watchdog_unsupported` — the scheduler could not safely host observation;
|
|
105
133
|
- `watchdog_disabled` — watchdog policy disabled observation.
|
|
106
134
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
135
|
+
Bounded `scheduler_stall_operation_overlap` events associate active operation
|
|
136
|
+
sequences with a stall on the same Thread. They establish temporal overlap, not
|
|
137
|
+
causality. Native work retaining Ruby's GVL can prevent the watchdog Thread from
|
|
138
|
+
running until the work returns.
|
|
139
|
+
|
|
140
|
+
### Long-active operation monitor
|
|
141
|
+
|
|
142
|
+
The independent operation-liveness monitor polls the bounded active-operation
|
|
143
|
+
registry. By default it polls every 100 ms and emits after an observed operation
|
|
144
|
+
remains active for strictly more than 1 second. All targeted operation types are
|
|
145
|
+
eligible, but entries beyond the snapshot bound can remain unobserved under
|
|
146
|
+
registry pressure; truncation is explicit and absence is not a coverage claim:
|
|
147
|
+
|
|
148
|
+
- `operation_liveness_active`;
|
|
149
|
+
- `operation_liveness_disabled`;
|
|
150
|
+
- `operation_liveness_unsupported`;
|
|
151
|
+
- `operation_long_active_started`;
|
|
152
|
+
- `operation_long_active_completed`.
|
|
153
|
+
|
|
154
|
+
Ordinary registry removal closes a pair with `operation_finished: true`.
|
|
155
|
+
Shutdown closes an open pair with `operation_finished: false`; that value does
|
|
156
|
+
not mean the application operation failed. Long-active evidence means only that
|
|
157
|
+
a targeted operation remained registered across the threshold. It is not a
|
|
158
|
+
scheduler stall, proven deadlock, or proof of scheduler harm.
|
|
159
|
+
|
|
160
|
+
Watchdog, liveness-state, overlap, and long-active events bypass random sampling
|
|
161
|
+
but still consume recorder rate, event, record-size, and session-size budgets.
|
|
162
|
+
Snapshot and per-poll truncation, drops, unsupported states, internal errors, and
|
|
163
|
+
incomplete sessions remain visible. A successful `exec` may intentionally leave
|
|
164
|
+
a session without `session_end`.
|
|
165
|
+
|
|
166
|
+
Rails execution contexts (`request`, `middleware`, `job`, and `websocket`) are
|
|
167
|
+
captured when Rails integration is active. Bounded immutable Fiber storage
|
|
168
|
+
propagates logical context to child Fibers without retaining request data.
|
|
169
|
+
Combined static/runtime reporting remains future work.
|
|
110
170
|
|
|
111
171
|
## Configuration
|
|
112
172
|
|
|
113
|
-
Copy `.fiber-audit.example.yml` to `.fiber-audit.yml
|
|
114
|
-
|
|
115
|
-
|
|
173
|
+
Copy `.fiber-audit.example.yml` to `.fiber-audit.yml`. Paths and globs are rooted
|
|
174
|
+
at the detected project; explicit `--config` is resolved from the invocation
|
|
175
|
+
directory.
|
|
116
176
|
|
|
117
177
|
```yaml
|
|
118
178
|
static:
|
|
119
|
-
include:
|
|
120
|
-
|
|
121
|
-
- lib/**/*.rb
|
|
122
|
-
- config/**/*.rb
|
|
123
|
-
exclude:
|
|
124
|
-
- vendor/**/*
|
|
125
|
-
- tmp/**/*
|
|
126
|
-
- db/schema.rb
|
|
179
|
+
include: [app/**/*.rb, lib/**/*.rb, config/**/*.rb]
|
|
180
|
+
exclude: [vendor/**/*, tmp/**/*, db/schema.rb]
|
|
127
181
|
suppressions_path: .fiber-audit-suppressions.yml
|
|
128
182
|
|
|
129
183
|
rules:
|
|
@@ -141,40 +195,45 @@ runtime:
|
|
|
141
195
|
mode: strict
|
|
142
196
|
sampling:
|
|
143
197
|
rate: 0.1
|
|
198
|
+
overhead:
|
|
199
|
+
max_events_per_second: 100
|
|
200
|
+
max_events_per_session: 10000
|
|
201
|
+
max_record_bytes: 16384
|
|
202
|
+
max_session_bytes: 10485760
|
|
144
203
|
watchdog:
|
|
145
204
|
enabled: true
|
|
146
205
|
heartbeat_interval_ms: 25
|
|
147
206
|
stall_threshold_ms: 100
|
|
148
207
|
max_frames: 20
|
|
208
|
+
operation_liveness:
|
|
209
|
+
enabled: true
|
|
210
|
+
poll_interval_ms: 100
|
|
211
|
+
long_active_threshold_ms: 1000
|
|
149
212
|
fail_open: true
|
|
150
213
|
```
|
|
151
214
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
215
|
+
Configuration is strict: unknown sections or keys, invalid types, and values
|
|
216
|
+
outside policy bounds return exit code 2. `--min-severity` overrides
|
|
217
|
+
`report.min_severity` for one static run. Severity ordering is `critical`,
|
|
218
|
+
`high`, `medium`, `low`, `info`; the default `low` threshold omits informational
|
|
219
|
+
findings.
|
|
156
220
|
|
|
157
221
|
## Suppressions
|
|
158
222
|
|
|
159
223
|
Every suppression requires a non-empty reason. Directive-looking text inside
|
|
160
224
|
strings, heredocs, or regular expressions is ignored.
|
|
161
225
|
|
|
162
|
-
Suppress one line:
|
|
163
|
-
|
|
164
226
|
```ruby
|
|
165
227
|
system(command) # fiber-audit:disable FA1001 -- trusted maintenance command
|
|
166
228
|
```
|
|
167
229
|
|
|
168
|
-
Suppress a block:
|
|
169
|
-
|
|
170
230
|
```ruby
|
|
171
231
|
# fiber-audit:disable FA1003 -- protected legacy boundary
|
|
172
232
|
mutex.synchronize { update_record }
|
|
173
233
|
# fiber-audit:enable FA1003
|
|
174
234
|
```
|
|
175
235
|
|
|
176
|
-
|
|
177
|
-
operation. Point `static.suppressions_path` at the file:
|
|
236
|
+
YAML suppressions can match rule and optionally symbol or operation:
|
|
178
237
|
|
|
179
238
|
```yaml
|
|
180
239
|
suppressions:
|
|
@@ -184,29 +243,23 @@ suppressions:
|
|
|
184
243
|
reason: isolated worker process with an external timeout
|
|
185
244
|
```
|
|
186
245
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
## Static statuses
|
|
246
|
+
## Static statuses and exit codes
|
|
190
247
|
|
|
191
248
|
- `FAIL` — at least one critical or high finding.
|
|
192
|
-
- `REVIEW` — a medium finding, or
|
|
193
|
-
finding.
|
|
249
|
+
- `REVIEW` — a medium finding, or non-informational low/unknown-confidence risk.
|
|
194
250
|
- `PASS_WITH_WARNINGS` — only low or informational findings.
|
|
195
251
|
- `NO_FINDINGS` — no findings at the configured threshold.
|
|
196
252
|
|
|
197
|
-
FiberAudit never emits unconditional `PASS
|
|
198
|
-
|
|
199
|
-
## Exit codes
|
|
253
|
+
FiberAudit never emits unconditional `PASS`.
|
|
200
254
|
|
|
201
255
|
| Code | Meaning |
|
|
202
256
|
|---|---|
|
|
203
257
|
| 0 | No active finding at or above the configured threshold |
|
|
204
258
|
| 1 | One or more active findings at or above the threshold |
|
|
205
259
|
| 2 | Invalid options, configuration, analysis, or report output |
|
|
206
|
-
| 3 | Reserved;
|
|
260
|
+
| 3 | Reserved; not emitted |
|
|
207
261
|
|
|
208
|
-
Source parse errors
|
|
209
|
-
other files.
|
|
262
|
+
Source parse errors remain report data while analysis continues on other files.
|
|
210
263
|
|
|
211
264
|
## Development and semantic verification
|
|
212
265
|
|
|
@@ -214,19 +267,19 @@ other files.
|
|
|
214
267
|
bundle exec rspec
|
|
215
268
|
bundle exec rubocop
|
|
216
269
|
bundle exec ruby script/scheduler-semantics
|
|
270
|
+
FIBER_AUDIT_BENCH_ITERATIONS=2000 bundle exec ruby benchmark/runtime_probe_overhead.rb
|
|
217
271
|
gem build fiber_audit.gemspec
|
|
218
272
|
bundle exec rake release:sanity
|
|
219
273
|
```
|
|
220
274
|
|
|
221
|
-
The semantic
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
Relevant Ruby contracts:
|
|
275
|
+
The semantic script uses bounded local Threads, pipes, localhost resolution, and
|
|
276
|
+
child processes. CI exercises it through the spec suite on tested CRuby versions.
|
|
277
|
+
It verifies only scheduler capabilities consumed by FiberAudit; it is not a full
|
|
278
|
+
scheduler-conformance suite.
|
|
226
279
|
|
|
227
|
-
|
|
228
|
-
-
|
|
229
|
-
|
|
280
|
+
The benchmark reports absent, installed/inactive, active sampling-zero, and
|
|
281
|
+
active sampling-one workloads in isolated subprocesses. It is diagnostic only:
|
|
282
|
+
there is no host-dependent CI timing threshold.
|
|
230
283
|
|
|
231
|
-
See [ARCHITECTURE.md](ARCHITECTURE.md) for implementation boundaries
|
|
232
|
-
|
|
284
|
+
See [ARCHITECTURE.md](ARCHITECTURE.md) for implementation boundaries and the
|
|
285
|
+
runtime truthfulness, privacy, lifecycle, and schema contracts.
|
data/lib/fiber_audit/cli.rb
CHANGED
|
@@ -101,6 +101,7 @@ module FiberAudit
|
|
|
101
101
|
environment = Runtime::Environment.child_environment(
|
|
102
102
|
settings: settings,
|
|
103
103
|
watchdog_policy: configuration.runtime_watchdog_policy,
|
|
104
|
+
operation_liveness_policy: configuration.runtime_operation_liveness_policy,
|
|
104
105
|
probes_enabled: true
|
|
105
106
|
)
|
|
106
107
|
Runtime::Supervisor.new(
|
|
@@ -202,7 +203,8 @@ module FiberAudit
|
|
|
202
203
|
min_severity: severity,
|
|
203
204
|
suppressions_path: configuration.suppressions_path,
|
|
204
205
|
runtime_policy: configuration.runtime_policy,
|
|
205
|
-
runtime_watchdog_policy: configuration.runtime_watchdog_policy
|
|
206
|
+
runtime_watchdog_policy: configuration.runtime_watchdog_policy,
|
|
207
|
+
runtime_operation_liveness_policy: configuration.runtime_operation_liveness_policy
|
|
206
208
|
)
|
|
207
209
|
end
|
|
208
210
|
|
|
@@ -6,6 +6,7 @@ require_relative 'errors'
|
|
|
6
6
|
require_relative 'findings/severity'
|
|
7
7
|
require_relative 'runtime/policy'
|
|
8
8
|
require_relative 'runtime/watchdog_policy'
|
|
9
|
+
require_relative 'runtime/operation_liveness_policy'
|
|
9
10
|
|
|
10
11
|
module FiberAudit
|
|
11
12
|
# rubocop:disable Metrics/ClassLength
|
|
@@ -14,7 +15,8 @@ module FiberAudit
|
|
|
14
15
|
KNOWN_STATIC_KEYS = %w[include exclude suppressions_path].freeze
|
|
15
16
|
KNOWN_REPORT_KEYS = %w[formats min_severity].freeze
|
|
16
17
|
KNOWN_RULE_KEYS = %w[enabled severity].freeze
|
|
17
|
-
KNOWN_RUNTIME_KEYS = %w[redaction sampling overhead watchdog fail_open].freeze
|
|
18
|
+
KNOWN_RUNTIME_KEYS = %w[redaction sampling overhead watchdog operation_liveness fail_open].freeze
|
|
19
|
+
KNOWN_OPERATION_LIVENESS_KEYS = %w[enabled poll_interval_ms long_active_threshold_ms].freeze
|
|
18
20
|
KNOWN_REDACTION_KEYS = %w[mode].freeze
|
|
19
21
|
KNOWN_SAMPLING_KEYS = %w[rate].freeze
|
|
20
22
|
KNOWN_OVERHEAD_KEYS = %w[
|
|
@@ -50,7 +52,8 @@ module FiberAudit
|
|
|
50
52
|
|
|
51
53
|
attr_reader :static_include, :static_exclude, :rules_config,
|
|
52
54
|
:report_formats, :min_severity, :suppressions_path,
|
|
53
|
-
:runtime_policy, :runtime_watchdog_policy
|
|
55
|
+
:runtime_policy, :runtime_watchdog_policy,
|
|
56
|
+
:runtime_operation_liveness_policy
|
|
54
57
|
|
|
55
58
|
def initialize(
|
|
56
59
|
static_include: DEFAULT_STATIC_INCLUDE,
|
|
@@ -60,12 +63,13 @@ module FiberAudit
|
|
|
60
63
|
min_severity: :low,
|
|
61
64
|
suppressions_path: nil,
|
|
62
65
|
runtime_policy: Runtime::Policy.new,
|
|
63
|
-
runtime_watchdog_policy: Runtime::WatchdogPolicy.new
|
|
66
|
+
runtime_watchdog_policy: Runtime::WatchdogPolicy.new,
|
|
67
|
+
runtime_operation_liveness_policy: Runtime::OperationLivenessPolicy.new
|
|
64
68
|
)
|
|
65
69
|
validate_types!(
|
|
66
70
|
static_include, static_exclude, rules_config,
|
|
67
71
|
report_formats, min_severity, suppressions_path, runtime_policy,
|
|
68
|
-
runtime_watchdog_policy
|
|
72
|
+
runtime_watchdog_policy, runtime_operation_liveness_policy
|
|
69
73
|
)
|
|
70
74
|
|
|
71
75
|
@static_include = static_include
|
|
@@ -76,6 +80,7 @@ module FiberAudit
|
|
|
76
80
|
@suppressions_path = suppressions_path
|
|
77
81
|
@runtime_policy = runtime_policy
|
|
78
82
|
@runtime_watchdog_policy = runtime_watchdog_policy
|
|
83
|
+
@runtime_operation_liveness_policy = runtime_operation_liveness_policy
|
|
79
84
|
end
|
|
80
85
|
|
|
81
86
|
def rule_enabled?(rule_id)
|
|
@@ -113,7 +118,8 @@ module FiberAudit
|
|
|
113
118
|
min_severity: report.fetch('min_severity', :low),
|
|
114
119
|
suppressions_path: static['suppressions_path'],
|
|
115
120
|
runtime_policy: runtime_policy_from(runtime),
|
|
116
|
-
runtime_watchdog_policy: runtime_watchdog_policy_from(runtime)
|
|
121
|
+
runtime_watchdog_policy: runtime_watchdog_policy_from(runtime),
|
|
122
|
+
runtime_operation_liveness_policy: runtime_operation_liveness_policy_from(runtime)
|
|
117
123
|
)
|
|
118
124
|
end
|
|
119
125
|
|
|
@@ -166,6 +172,7 @@ module FiberAudit
|
|
|
166
172
|
validate_runtime_mapping!(runtime, 'sampling', KNOWN_SAMPLING_KEYS)
|
|
167
173
|
validate_runtime_mapping!(runtime, 'overhead', KNOWN_OVERHEAD_KEYS)
|
|
168
174
|
validate_runtime_mapping!(runtime, 'watchdog', KNOWN_WATCHDOG_KEYS)
|
|
175
|
+
validate_runtime_mapping!(runtime, 'operation_liveness', KNOWN_OPERATION_LIVENESS_KEYS)
|
|
169
176
|
end
|
|
170
177
|
|
|
171
178
|
def validate_runtime_mapping!(runtime, key, allowed)
|
|
@@ -214,6 +221,19 @@ module FiberAudit
|
|
|
214
221
|
raise ConfigurationError, "#{path} is invalid: #{e.message}"
|
|
215
222
|
end
|
|
216
223
|
|
|
224
|
+
def runtime_operation_liveness_policy_from(runtime)
|
|
225
|
+
values = runtime.fetch('operation_liveness', {})
|
|
226
|
+
defaults = Runtime::OperationLivenessPolicy::DEFAULTS
|
|
227
|
+
Runtime::OperationLivenessPolicy.new(
|
|
228
|
+
enabled: values.fetch('enabled', defaults[:enabled]),
|
|
229
|
+
poll_interval_ms: values.fetch('poll_interval_ms', defaults[:poll_interval_ms]),
|
|
230
|
+
long_active_threshold_ms: values.fetch('long_active_threshold_ms', defaults[:long_active_threshold_ms])
|
|
231
|
+
)
|
|
232
|
+
rescue RuntimeContractError => e
|
|
233
|
+
field = e.message.split.first
|
|
234
|
+
raise ConfigurationError, "runtime.operation_liveness.#{field} is invalid: #{e.message}"
|
|
235
|
+
end
|
|
236
|
+
|
|
217
237
|
def check_unknown_keys(hash, allowed, path)
|
|
218
238
|
unknown = hash.keys - allowed
|
|
219
239
|
return if unknown.empty?
|
|
@@ -229,7 +249,8 @@ module FiberAudit
|
|
|
229
249
|
|
|
230
250
|
def validate_types!(
|
|
231
251
|
include_patterns, exclude_patterns, rules,
|
|
232
|
-
formats, _severity, suppressions, runtime_policy, watchdog_policy
|
|
252
|
+
formats, _severity, suppressions, runtime_policy, watchdog_policy,
|
|
253
|
+
operation_liveness_policy
|
|
233
254
|
)
|
|
234
255
|
unless include_patterns.is_a?(Array) &&
|
|
235
256
|
include_patterns.all?(String)
|
|
@@ -256,6 +277,10 @@ module FiberAudit
|
|
|
256
277
|
raise ConfigurationError,
|
|
257
278
|
'runtime_watchdog_policy must be a FiberAudit::Runtime::WatchdogPolicy'
|
|
258
279
|
end
|
|
280
|
+
unless operation_liveness_policy.is_a?(Runtime::OperationLivenessPolicy)
|
|
281
|
+
raise ConfigurationError,
|
|
282
|
+
'runtime_operation_liveness_policy must be a FiberAudit::Runtime::OperationLivenessPolicy'
|
|
283
|
+
end
|
|
259
284
|
|
|
260
285
|
return if suppressions.nil? || suppressions.is_a?(String)
|
|
261
286
|
|