fiber_audit 0.1.0 → 0.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/.fiber-audit.example.yml +19 -0
- data/ARCHITECTURE.md +726 -0
- data/CHANGELOG.md +41 -0
- data/LICENSE +201 -0
- data/README.md +66 -8
- data/lib/fiber_audit/cli.rb +87 -2
- data/lib/fiber_audit/configuration.rb +106 -6
- data/lib/fiber_audit/errors.rb +2 -0
- data/lib/fiber_audit/operation_vocabulary.rb +42 -0
- data/lib/fiber_audit/reporters/text.rb +1 -1
- data/lib/fiber_audit/runtime/active_operations.rb +146 -0
- data/lib/fiber_audit/runtime/boot.rb +83 -0
- data/lib/fiber_audit/runtime/clock.rb +35 -0
- data/lib/fiber_audit/runtime/environment.rb +289 -0
- data/lib/fiber_audit/runtime/event.rb +86 -0
- data/lib/fiber_audit/runtime/execution_context.rb +89 -0
- data/lib/fiber_audit/runtime/heartbeat.rb +113 -0
- data/lib/fiber_audit/runtime/jsonl/schema.rb +312 -0
- data/lib/fiber_audit/runtime/jsonl/writer.rb +122 -0
- data/lib/fiber_audit/runtime/lifecycle.rb +343 -0
- data/lib/fiber_audit/runtime/limits.rb +102 -0
- data/lib/fiber_audit/runtime/location.rb +44 -0
- data/lib/fiber_audit/runtime/policy.rb +121 -0
- data/lib/fiber_audit/runtime/probes/base.rb +333 -0
- data/lib/fiber_audit/runtime/probes/http.rb +80 -0
- data/lib/fiber_audit/runtime/probes/io_select.rb +50 -0
- data/lib/fiber_audit/runtime/probes/registry.rb +156 -0
- data/lib/fiber_audit/runtime/probes/socket.rb +76 -0
- data/lib/fiber_audit/runtime/probes/subprocess.rb +83 -0
- data/lib/fiber_audit/runtime/probes/synchronization.rb +58 -0
- data/lib/fiber_audit/runtime/probes/thread_state.rb +39 -0
- data/lib/fiber_audit/runtime/probes/thread_wait.rb +25 -0
- data/lib/fiber_audit/runtime/rails_integration.rb +320 -0
- data/lib/fiber_audit/runtime/recorder.rb +333 -0
- data/lib/fiber_audit/runtime/redactor.rb +102 -0
- data/lib/fiber_audit/runtime/sampler.rb +26 -0
- data/lib/fiber_audit/runtime/scheduler_observer.rb +128 -0
- data/lib/fiber_audit/runtime/session.rb +112 -0
- data/lib/fiber_audit/runtime/supervisor.rb +114 -0
- data/lib/fiber_audit/runtime/validation.rb +68 -0
- data/lib/fiber_audit/runtime/watchdog.rb +479 -0
- data/lib/fiber_audit/runtime/watchdog_policy.rb +64 -0
- data/lib/fiber_audit/runtime.rb +37 -0
- data/lib/fiber_audit/static/rules/blocking_subprocess.rb +3 -8
- data/lib/fiber_audit/static/rules/direct_socket.rb +2 -3
- data/lib/fiber_audit/static/rules/io_select.rb +2 -4
- data/lib/fiber_audit/static/rules/net_http_in_request.rb +3 -5
- data/lib/fiber_audit/static/rules/synchronization.rb +2 -6
- data/lib/fiber_audit/static/rules/thread_current_state.rb +3 -2
- data/lib/fiber_audit/static/rules/thread_join.rb +3 -2
- data/lib/fiber_audit/version.rb +1 -1
- data/lib/fiber_audit.rb +1 -0
- metadata +38 -2
data/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
# FiberAudit Architecture
|
|
2
|
+
|
|
3
|
+
## 1. Purpose
|
|
4
|
+
|
|
5
|
+
FiberAudit is a risk-based compatibility auditor for Ruby and Rails applications
|
|
6
|
+
running in a fiber-scheduled environment, such as a Rails application served by
|
|
7
|
+
Falcon.
|
|
8
|
+
|
|
9
|
+
It is designed to answer:
|
|
10
|
+
|
|
11
|
+
1. Which operations may block a scheduler thread?
|
|
12
|
+
2. Where do those operations occur?
|
|
13
|
+
3. Do they appear in request, middleware, callback, job, WebSocket, boot, or
|
|
14
|
+
other execution contexts?
|
|
15
|
+
4. How strong is the evidence for each finding?
|
|
16
|
+
5. What should an application owner review or remediate?
|
|
17
|
+
|
|
18
|
+
FiberAudit does **not** prove that an application is fiber-safe. Static analysis
|
|
19
|
+
produces hypotheses, while observational runtime sessions provide bounded evidence
|
|
20
|
+
without establishing complete coverage. FiberAudit never claims unconditional
|
|
21
|
+
`PASS`.
|
|
22
|
+
|
|
23
|
+
> **Repository status:** v0.2.1 includes the v0.1.0 static pipeline end to end:
|
|
24
|
+
> project discovery, configuration, semantic and syntax analysis, execution
|
|
25
|
+
> contexts, FA1001–FA1007, suppressions, status derivation, text/JSON reports,
|
|
26
|
+
> and the CLI. The v0.2 runtime contracts, bounded JSONL recorder, explicit
|
|
27
|
+
> child-process boot, lifecycle, supervising command, bounded scheduler
|
|
28
|
+
> watchdog, targeted FA1001–FA1007 operation probes, and Rails runtime execution
|
|
29
|
+
> contexts are implemented; static/runtime correlation remains future work.
|
|
30
|
+
|
|
31
|
+
## 2. Scope
|
|
32
|
+
|
|
33
|
+
### v0.1.0
|
|
34
|
+
|
|
35
|
+
The v0.1.0 architecture is static-only:
|
|
36
|
+
|
|
37
|
+
- inspect Ruby source with Rubydex and Prism;
|
|
38
|
+
- identify high-signal operations that may block or misuse thread-local state;
|
|
39
|
+
- classify findings by execution context where possible;
|
|
40
|
+
- attach severity, confidence, evidence, remediation, and a stable fingerprint;
|
|
41
|
+
- apply inline and YAML suppressions;
|
|
42
|
+
- emit text and versioned JSON reports;
|
|
43
|
+
- return CI-friendly exit codes.
|
|
44
|
+
|
|
45
|
+
### Explicitly outside v0.1.0
|
|
46
|
+
|
|
47
|
+
The following belong to later releases:
|
|
48
|
+
|
|
49
|
+
- runtime instrumentation and scheduler watchdogs;
|
|
50
|
+
- Rails Railties and middleware;
|
|
51
|
+
- Falcon process orchestration;
|
|
52
|
+
- static/runtime correlation beyond stable fingerprint generation;
|
|
53
|
+
- dependency compatibility intelligence;
|
|
54
|
+
- SARIF and HTML reporters;
|
|
55
|
+
- runtime-backed `PASS` certification.
|
|
56
|
+
|
|
57
|
+
## 3. Sources of Truth
|
|
58
|
+
|
|
59
|
+
Current code and specs define implementation truth. This document records the
|
|
60
|
+
supported architecture and dependency boundaries; README and CHANGELOG record
|
|
61
|
+
the user-facing release contract.
|
|
62
|
+
|
|
63
|
+
The corrected platform target is Ruby `>= 3.3` with CI configured for Ruby 3.3
|
|
64
|
+
and 3.4. Ruby 3.2 is excluded because it is end-of-life.
|
|
65
|
+
|
|
66
|
+
## 4. Architectural Principles
|
|
67
|
+
|
|
68
|
+
### 4.1 FiberAudit owns its public contracts
|
|
69
|
+
|
|
70
|
+
Rubydex and Prism are implementation dependencies. Their objects must not leak
|
|
71
|
+
into rules, findings, suppressions, or reporters. Adapters translate external
|
|
72
|
+
library data into FiberAudit-owned value objects.
|
|
73
|
+
|
|
74
|
+
### 4.2 Severity and confidence are separate
|
|
75
|
+
|
|
76
|
+
- **Severity** represents impact if the finding is real.
|
|
77
|
+
- **Confidence** represents the strength of the evidence.
|
|
78
|
+
|
|
79
|
+
A high-impact heuristic can therefore be `severity: :high` and
|
|
80
|
+
`confidence: :low` without conflating the two dimensions.
|
|
81
|
+
|
|
82
|
+
### 4.3 Findings are the integration boundary
|
|
83
|
+
|
|
84
|
+
Static rules—and future runtime correlation—use the same `Finding` model.
|
|
85
|
+
Suppressions, status derivation, reporters, and future correlation operate on
|
|
86
|
+
findings rather than AST or semantic-index objects.
|
|
87
|
+
|
|
88
|
+
### 4.4 Analysis degrades gracefully
|
|
89
|
+
|
|
90
|
+
An unresolved constant, unknown execution context, or unsupported Rubydex query
|
|
91
|
+
should lower confidence or produce explicit gap metadata. It should not crash an
|
|
92
|
+
entire project audit unless analysis cannot continue safely.
|
|
93
|
+
|
|
94
|
+
### 4.5 Suppression is post-analysis filtering
|
|
95
|
+
|
|
96
|
+
Rules produce findings independently of suppression policy. The suppression
|
|
97
|
+
store partitions findings into active and suppressed sets. Suppressed findings
|
|
98
|
+
remain available for reporting and audit history, but do not determine the
|
|
99
|
+
active result or exit status.
|
|
100
|
+
|
|
101
|
+
### 4.6 Static analysis cannot grant `PASS`
|
|
102
|
+
|
|
103
|
+
The strongest static-only outcomes are `NO_FINDINGS` or
|
|
104
|
+
`PASS_WITH_WARNINGS`, accompanied by a static-only disclaimer. A plain `PASS`
|
|
105
|
+
requires sufficient runtime coverage and is outside v0.1.0.
|
|
106
|
+
|
|
107
|
+
## 5. System Context
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
Ruby/Rails project
|
|
111
|
+
source files
|
|
112
|
+
configuration
|
|
113
|
+
suppressions
|
|
114
|
+
|
|
|
115
|
+
v
|
|
116
|
+
+--------------------------+
|
|
117
|
+
| FiberAudit static audit |
|
|
118
|
+
| |
|
|
119
|
+
| Rubydex semantic data |
|
|
120
|
+
| Prism syntax data |
|
|
121
|
+
| Context classification |
|
|
122
|
+
| Static rules |
|
|
123
|
+
| Findings + suppressions |
|
|
124
|
+
+-------------+------------+
|
|
125
|
+
|
|
|
126
|
+
+--> Text report
|
|
127
|
+
+--> JSON report
|
|
128
|
+
+--> Process exit code
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
FiberAudit does not load Rails at the gem entry point. Rails-shaped semantics
|
|
132
|
+
are inferred from source structure, inheritance, paths, and callbacks.
|
|
133
|
+
|
|
134
|
+
## 6. Current Repository Architecture
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
fiber-audit static
|
|
138
|
+
|
|
|
139
|
+
+-- Project + Configuration
|
|
140
|
+
+-- SemanticIndex + CallSiteExtractor
|
|
141
|
+
+-- ExecutionContextResolver
|
|
142
|
+
+-- Built-in registry (FA1001–FA1007)
|
|
143
|
+
+-- Findings + Suppression Store
|
|
144
|
+
+-- Audit::Result
|
|
145
|
+
+-- Text or JSON reporter
|
|
146
|
+
+-- Exit code 0, 1, or 2
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Current component status
|
|
150
|
+
|
|
151
|
+
| Component | Responsibility | Status |
|
|
152
|
+
|---|---|---|
|
|
153
|
+
| Gem packaging and loader | Ruby baseline, executable, public surface | Implemented |
|
|
154
|
+
| CLI and project discovery | Commands, root/config resolution, exit codes | Implemented |
|
|
155
|
+
| Findings and fingerprints | Evidence-bearing values and stable identity | Implemented |
|
|
156
|
+
| Configuration and suppressions | Validation and post-analysis filtering | Implemented |
|
|
157
|
+
| `SemanticIndex` | Rubydex adapter | Implemented |
|
|
158
|
+
| `CallSiteExtractor` | Prism traversal and conservative inference | Implemented |
|
|
159
|
+
| Context resolver | Rails/request/job/etc. classification | Implemented |
|
|
160
|
+
| Rule system | Registry and FA1001–FA1007 | Implemented |
|
|
161
|
+
| Audit coordinator | End-to-end orchestration and status | Implemented |
|
|
162
|
+
| Reporters | Text and JSON schema 1.0 | Implemented |
|
|
163
|
+
| Runtime engine | Bounded sessions, watchdog, targeted operations | Implemented through Stage 5 |
|
|
164
|
+
|
|
165
|
+
The public loader is `lib/fiber_audit.rb`.
|
|
166
|
+
|
|
167
|
+
## 7. v0.1.0 Static Pipeline
|
|
168
|
+
|
|
169
|
+
The pipeline below is implemented and covered by unit, fixture, CLI, and golden
|
|
170
|
+
report tests.
|
|
171
|
+
|
|
172
|
+
```text
|
|
173
|
+
CLI / Project discovery
|
|
174
|
+
|
|
|
175
|
+
v
|
|
176
|
+
Configuration + source glob expansion
|
|
177
|
+
|
|
|
178
|
+
+----------------------+
|
|
179
|
+
| |
|
|
180
|
+
v v
|
|
181
|
+
Rubydex SemanticIndex Prism CallSiteExtractor
|
|
182
|
+
(project-wide meaning) (local syntax and calls)
|
|
183
|
+
| |
|
|
184
|
+
+----------+-----------+
|
|
185
|
+
|
|
|
186
|
+
v
|
|
187
|
+
ExecutionContextResolver
|
|
188
|
+
|
|
|
189
|
+
v
|
|
190
|
+
Enabled rules FA1001–FA1007
|
|
191
|
+
|
|
|
192
|
+
v
|
|
193
|
+
Findings
|
|
194
|
+
|
|
|
195
|
+
v
|
|
196
|
+
Inline/YAML Suppression Store
|
|
197
|
+
|
|
|
198
|
+
+--------+---------+
|
|
199
|
+
| |
|
|
200
|
+
v v
|
|
201
|
+
Active findings Suppressed findings
|
|
202
|
+
| |
|
|
203
|
+
+--------+---------+
|
|
204
|
+
|
|
|
205
|
+
v
|
|
206
|
+
Audit::Result
|
|
207
|
+
|
|
|
208
|
+
+--------+---------+
|
|
209
|
+
| |
|
|
210
|
+
v v
|
|
211
|
+
Text reporter JSON reporter
|
|
212
|
+
| |
|
|
213
|
+
+--------+---------+
|
|
214
|
+
|
|
|
215
|
+
v
|
|
216
|
+
Exit status
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Pipeline behavior
|
|
220
|
+
|
|
221
|
+
1. Detect the project root and configuration file.
|
|
222
|
+
2. Validate configuration before analysis starts.
|
|
223
|
+
3. Expand included Ruby files and remove excluded paths.
|
|
224
|
+
4. Build the Rubydex semantic index once for the workspace.
|
|
225
|
+
5. Parse each selected source file once with Prism.
|
|
226
|
+
6. Convert call nodes into FiberAudit-owned `CallSite` values.
|
|
227
|
+
7. Resolve receivers and execution contexts on a best-effort basis.
|
|
228
|
+
8. Run enabled rules over call sites.
|
|
229
|
+
9. Publish evidence-bearing findings to a collection.
|
|
230
|
+
10. Parse and apply inline and YAML suppressions.
|
|
231
|
+
11. Derive the project status from active findings.
|
|
232
|
+
12. Render the selected format and return the configured exit status.
|
|
233
|
+
|
|
234
|
+
## 8. Component Boundaries
|
|
235
|
+
|
|
236
|
+
### 8.1 CLI and project discovery
|
|
237
|
+
|
|
238
|
+
**Current files**
|
|
239
|
+
|
|
240
|
+
- `lib/fiber_audit/cli.rb`
|
|
241
|
+
- `lib/fiber_audit/project.rb`
|
|
242
|
+
|
|
243
|
+
Responsibilities:
|
|
244
|
+
|
|
245
|
+
- parse commands and flags;
|
|
246
|
+
- discover the project root;
|
|
247
|
+
- locate `.fiber-audit.yml` or honor `--config`;
|
|
248
|
+
- invoke `Audit`;
|
|
249
|
+
- select a reporter;
|
|
250
|
+
- map results and errors to process exit codes.
|
|
251
|
+
|
|
252
|
+
The CLI must not contain AST traversal, rule matching, or report assembly logic.
|
|
253
|
+
|
|
254
|
+
### 8.2 Configuration
|
|
255
|
+
|
|
256
|
+
**Current file:** `lib/fiber_audit/configuration.rb`
|
|
257
|
+
|
|
258
|
+
Recognized static configuration shape:
|
|
259
|
+
|
|
260
|
+
```yaml
|
|
261
|
+
static:
|
|
262
|
+
include:
|
|
263
|
+
- app/**/*.rb
|
|
264
|
+
- lib/**/*.rb
|
|
265
|
+
- config/**/*.rb
|
|
266
|
+
exclude:
|
|
267
|
+
- vendor/**/*
|
|
268
|
+
- tmp/**/*
|
|
269
|
+
- db/schema.rb
|
|
270
|
+
suppressions_path: .fiber-audit-suppressions.yml
|
|
271
|
+
|
|
272
|
+
rules:
|
|
273
|
+
FA1001:
|
|
274
|
+
enabled: true
|
|
275
|
+
severity: high
|
|
276
|
+
|
|
277
|
+
report:
|
|
278
|
+
formats:
|
|
279
|
+
- text
|
|
280
|
+
- json
|
|
281
|
+
min_severity: low
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The configuration boundary owns defaults, type validation, allowed formats,
|
|
285
|
+
severity coercion, and rule overrides. Configuration failures must become a
|
|
286
|
+
FiberAudit configuration error so the CLI can consistently exit with code 2.
|
|
287
|
+
|
|
288
|
+
### 8.3 Semantic indexing
|
|
289
|
+
|
|
290
|
+
**Current file:** `lib/fiber_audit/static/semantic_index.rb`
|
|
291
|
+
|
|
292
|
+
Rubydex supplies project-wide information:
|
|
293
|
+
|
|
294
|
+
- declarations;
|
|
295
|
+
- constant resolution;
|
|
296
|
+
- ancestry and descendants;
|
|
297
|
+
- constant references;
|
|
298
|
+
- source locations.
|
|
299
|
+
|
|
300
|
+
`SemanticIndex` is the only component that should directly depend on
|
|
301
|
+
`Rubydex::Graph`. It normalizes workspace paths and coordinates and returns
|
|
302
|
+
FiberAudit-owned values:
|
|
303
|
+
|
|
304
|
+
- `Declaration`
|
|
305
|
+
- `Reference`
|
|
306
|
+
- `Constant`
|
|
307
|
+
- `RubydexGap`
|
|
308
|
+
|
|
309
|
+
Known adapter limitations are exposed as `RubydexGap` values and documented by
|
|
310
|
+
the Rubydex spike fixtures.
|
|
311
|
+
|
|
312
|
+
### 8.4 Syntax and call-site extraction
|
|
313
|
+
|
|
314
|
+
**Current files:**
|
|
315
|
+
|
|
316
|
+
- `lib/fiber_audit/static/call_site.rb`
|
|
317
|
+
- `lib/fiber_audit/static/call_site_extractor.rb`
|
|
318
|
+
|
|
319
|
+
Prism supplies syntax-level information that Rubydex does not reliably expose,
|
|
320
|
+
including method names, receiver source, arguments, lexical nesting, and
|
|
321
|
+
precise call-site locations.
|
|
322
|
+
|
|
323
|
+
The target `CallSite` contract contains:
|
|
324
|
+
|
|
325
|
+
```text
|
|
326
|
+
path, line, column
|
|
327
|
+
receiver_source, receiver_constant, method_name
|
|
328
|
+
arguments, enclosing_symbol, nesting
|
|
329
|
+
execution_context, resolution, confidence
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
The extractor parses each file once and performs conservative receiver
|
|
333
|
+
inference. The obsolete `SourceIndex` placeholder has been removed.
|
|
334
|
+
|
|
335
|
+
### 8.5 Execution-context resolution
|
|
336
|
+
|
|
337
|
+
**Current files:**
|
|
338
|
+
|
|
339
|
+
- `lib/fiber_audit/execution_context.rb`
|
|
340
|
+
- `lib/fiber_audit/static/execution_context_resolver.rb`
|
|
341
|
+
|
|
342
|
+
Supported contexts are planned as:
|
|
343
|
+
|
|
344
|
+
```text
|
|
345
|
+
request, middleware, callback, view, job, websocket,
|
|
346
|
+
boot, console, rake_task, test, unknown
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Resolution order:
|
|
350
|
+
|
|
351
|
+
1. semantic inheritance;
|
|
352
|
+
2. path-based fallback;
|
|
353
|
+
3. callback/DSL syntax hints;
|
|
354
|
+
4. `unknown`.
|
|
355
|
+
|
|
356
|
+
The resolver must never invent certainty. Unknown or heuristic contexts remain
|
|
357
|
+
explicit and can lower confidence.
|
|
358
|
+
|
|
359
|
+
### 8.6 Static rule system
|
|
360
|
+
|
|
361
|
+
**Current files:** `lib/fiber_audit/static/rules/`
|
|
362
|
+
|
|
363
|
+
Rules consume FiberAudit `CallSite` values and emit `Finding` values. They do
|
|
364
|
+
not parse files and do not access Rubydex directly.
|
|
365
|
+
|
|
366
|
+
Shipped v0.1.0 rules:
|
|
367
|
+
|
|
368
|
+
| ID | Concern | Default severity |
|
|
369
|
+
|---|---|---:|
|
|
370
|
+
| FA1001 | Blocking subprocess operations | high |
|
|
371
|
+
| FA1002 | `Thread#join` / `Thread#value` | high |
|
|
372
|
+
| FA1003 | Blocking synchronization | medium |
|
|
373
|
+
| FA1004 | Thread-local request state | medium/high by operation |
|
|
374
|
+
| FA1005 | Explicit `IO.select` | medium |
|
|
375
|
+
| FA1006 | Direct socket creation | medium |
|
|
376
|
+
| FA1007 | `Net::HTTP` in request-like contexts | high |
|
|
377
|
+
|
|
378
|
+
A rule registry owns registration, enumeration, configuration enablement, and
|
|
379
|
+
metadata used by `list-rules` and `explain`.
|
|
380
|
+
|
|
381
|
+
### 8.7 Findings and fingerprints
|
|
382
|
+
|
|
383
|
+
**Current files:**
|
|
384
|
+
|
|
385
|
+
- `lib/fiber_audit/findings/location.rb`
|
|
386
|
+
- `lib/fiber_audit/findings/evidence.rb`
|
|
387
|
+
- `lib/fiber_audit/findings/finding.rb`
|
|
388
|
+
- `lib/fiber_audit/findings/collection.rb`
|
|
389
|
+
- `lib/fiber_audit/findings/severity.rb`
|
|
390
|
+
- `lib/fiber_audit/findings/confidence.rb`
|
|
391
|
+
- `lib/fiber_audit/correlation/fingerprint.rb`
|
|
392
|
+
|
|
393
|
+
Severity order:
|
|
394
|
+
|
|
395
|
+
```text
|
|
396
|
+
critical > high > medium > low > info
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Confidence order:
|
|
400
|
+
|
|
401
|
+
```text
|
|
402
|
+
confirmed > high > medium > low > unknown
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
A finding carries:
|
|
406
|
+
|
|
407
|
+
```text
|
|
408
|
+
rule identity and title
|
|
409
|
+
category
|
|
410
|
+
severity and confidence
|
|
411
|
+
location and enclosing symbol
|
|
412
|
+
resolved operation and execution context
|
|
413
|
+
message, evidence, and remediation
|
|
414
|
+
stable fingerprint
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
The fingerprint is SHA-256 over:
|
|
418
|
+
|
|
419
|
+
```text
|
|
420
|
+
rule_id : normalized_path : enclosing_symbol : operation
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
Line number is intentionally excluded so a finding remains stable when nearby
|
|
424
|
+
source lines move.
|
|
425
|
+
|
|
426
|
+
`Finding.new` currently permits empty evidence while a finding is assembled.
|
|
427
|
+
Publication through a collection is intended to require at least one evidence
|
|
428
|
+
entry. Constructor and publication paths must enforce the same final invariant.
|
|
429
|
+
|
|
430
|
+
### 8.8 Suppressions
|
|
431
|
+
|
|
432
|
+
**Current files:**
|
|
433
|
+
|
|
434
|
+
- `lib/fiber_audit/suppressions/parser.rb`
|
|
435
|
+
- `lib/fiber_audit/suppressions/store.rb`
|
|
436
|
+
|
|
437
|
+
Inline form:
|
|
438
|
+
|
|
439
|
+
```ruby
|
|
440
|
+
# fiber-audit:disable FA1001 -- executed only by an offline migration
|
|
441
|
+
Open3.capture3(command)
|
|
442
|
+
# fiber-audit:enable FA1001
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
YAML form:
|
|
446
|
+
|
|
447
|
+
```yaml
|
|
448
|
+
suppressions:
|
|
449
|
+
- rule: FA1001
|
|
450
|
+
symbol: DataMigration#run
|
|
451
|
+
reason: Executed only by an offline task
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Every suppression requires a reason. Inline directives must come from actual
|
|
455
|
+
Ruby comments; directive-looking text in strings, heredocs, or regular
|
|
456
|
+
expressions is not a directive. Both disable and enable matching must use
|
|
457
|
+
comment locations rather than unrestricted line scans.
|
|
458
|
+
|
|
459
|
+
### 8.9 Audit coordinator
|
|
460
|
+
|
|
461
|
+
**Current file:** `lib/fiber_audit/audit.rb`
|
|
462
|
+
|
|
463
|
+
The coordinator owns pipeline sequencing, not component internals. Its result
|
|
464
|
+
must contain enough information for all reporters:
|
|
465
|
+
|
|
466
|
+
- active findings;
|
|
467
|
+
- suppressed findings;
|
|
468
|
+
- parse/analysis errors;
|
|
469
|
+
- derived status;
|
|
470
|
+
- static-only disclaimer and coverage metadata.
|
|
471
|
+
|
|
472
|
+
### 8.10 Reporters
|
|
473
|
+
|
|
474
|
+
**Current files:** `lib/fiber_audit/reporters/`
|
|
475
|
+
|
|
476
|
+
Reporters consume `Audit::Result`; they do not rerun analysis or apply
|
|
477
|
+
suppressions.
|
|
478
|
+
|
|
479
|
+
- Text output is optimized for humans and CI logs.
|
|
480
|
+
- JSON output is a versioned external contract.
|
|
481
|
+
- The planned initial JSON schema version is `1.0`.
|
|
482
|
+
|
|
483
|
+
`Finding#to_h_for_json` is currently an internal serialization helper. It does
|
|
484
|
+
not by itself constitute the complete versioned report schema.
|
|
485
|
+
|
|
486
|
+
## 9. Dependency Direction
|
|
487
|
+
|
|
488
|
+
The desired dependency direction is inward toward FiberAudit-owned contracts:
|
|
489
|
+
|
|
490
|
+
```text
|
|
491
|
+
CLI / Reporters
|
|
492
|
+
|
|
|
493
|
+
v
|
|
494
|
+
Audit coordinator
|
|
495
|
+
|
|
|
496
|
+
+--> Suppression Store
|
|
497
|
+
+--> Rule Registry
|
|
498
|
+
|
|
|
499
|
+
v
|
|
500
|
+
CallSite
|
|
501
|
+
^
|
|
502
|
+
|
|
|
503
|
+
Context Resolver / Extractor
|
|
504
|
+
^ ^
|
|
505
|
+
| |
|
|
506
|
+
SemanticIndex Prism syntax
|
|
507
|
+
|
|
|
508
|
+
Rubydex graph
|
|
509
|
+
|
|
510
|
+
Rules --------------------------> Finding
|
|
511
|
+
Suppressions / Reporters -------> Finding
|
|
512
|
+
Finding ------------------------> Fingerprint, Location, Evidence
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Forbidden dependencies:
|
|
516
|
+
|
|
517
|
+
- rules must not depend on Rubydex or Prism node classes;
|
|
518
|
+
- reporters must not depend on indexes or rules;
|
|
519
|
+
- suppressions must not mutate rule behavior;
|
|
520
|
+
- the top-level gem loader must not require Rails;
|
|
521
|
+
- v0.1.0 code must not depend on runtime instrumentation modules.
|
|
522
|
+
|
|
523
|
+
## 10. Status and Exit Contracts
|
|
524
|
+
|
|
525
|
+
### Planned project statuses
|
|
526
|
+
|
|
527
|
+
| Status | Meaning |
|
|
528
|
+
|---|---|
|
|
529
|
+
| `FAIL` | At least one active critical or high finding |
|
|
530
|
+
| `REVIEW` | Medium risk, or unresolved low/unknown-confidence risk requiring review |
|
|
531
|
+
| `PASS_WITH_WARNINGS` | Only low or informational findings |
|
|
532
|
+
| `NO_FINDINGS` | No active findings |
|
|
533
|
+
|
|
534
|
+
Every v0.1.0 report must include:
|
|
535
|
+
|
|
536
|
+
> This is a static-only audit. PASS cannot be granted without runtime coverage.
|
|
537
|
+
|
|
538
|
+
### Exit codes
|
|
539
|
+
|
|
540
|
+
| Code | Meaning |
|
|
541
|
+
|---:|---|
|
|
542
|
+
| 0 | No active finding at or above the configured threshold |
|
|
543
|
+
| 1 | At least one active finding at or above the threshold |
|
|
544
|
+
| 2 | Configuration or analysis error |
|
|
545
|
+
| 3 | Reserved; not emitted in v0.1.0 |
|
|
546
|
+
|
|
547
|
+
The `static` command implements these result and exit-code semantics. Source
|
|
548
|
+
parse errors remain report data so analysis can continue on other files.
|
|
549
|
+
|
|
550
|
+
## 11. Error Handling
|
|
551
|
+
|
|
552
|
+
Errors should be split into two classes of behavior:
|
|
553
|
+
|
|
554
|
+
- **Recoverable analysis gaps:** record an error/gap, lower confidence, and
|
|
555
|
+
continue with other files or references.
|
|
556
|
+
- **Invalid invocation or configuration:** raise a FiberAudit-owned error and
|
|
557
|
+
let the CLI return exit code 2.
|
|
558
|
+
|
|
559
|
+
External exceptions should be translated at their adapter boundary. Downstream
|
|
560
|
+
components should not need to rescue Rubydex-, Prism-, or YAML-specific errors.
|
|
561
|
+
|
|
562
|
+
Shared error-class placement remains an R1 design decision; the remediation
|
|
563
|
+
plan recommends a dedicated `lib/fiber_audit/errors.rb`.
|
|
564
|
+
|
|
565
|
+
## 12. Testing Architecture
|
|
566
|
+
|
|
567
|
+
Tests mirror `lib/` under `spec/`.
|
|
568
|
+
|
|
569
|
+
### Current coverage
|
|
570
|
+
|
|
571
|
+
The suite covers value objects, semantic adaptation, call-site extraction,
|
|
572
|
+
context resolution, all built-in rules, configuration, suppressions, project
|
|
573
|
+
discovery, orchestration, reporters, CLI exit paths, and a versioned golden
|
|
574
|
+
report.
|
|
575
|
+
|
|
576
|
+
### v0.1.0 coverage
|
|
577
|
+
|
|
578
|
+
- exact `CallSite` extraction and receiver inference;
|
|
579
|
+
- execution-context classification;
|
|
580
|
+
- positive and negative fixtures for every rule;
|
|
581
|
+
- shadowed-constant negatives to avoid name-only false positives;
|
|
582
|
+
- stable fingerprints across repeated analysis;
|
|
583
|
+
- suppression behavior, including comment-only enable/disable directives;
|
|
584
|
+
- project-root and configuration discovery;
|
|
585
|
+
- text and JSON reporter contracts;
|
|
586
|
+
- golden JSON output;
|
|
587
|
+
- clean, findings, and invalid-config CLI exit paths.
|
|
588
|
+
|
|
589
|
+
Fixture applications should remain small and deterministic:
|
|
590
|
+
|
|
591
|
+
```text
|
|
592
|
+
spec/fixtures/apps/poro_clean
|
|
593
|
+
spec/fixtures/apps/rails_blockers
|
|
594
|
+
spec/fixtures/apps/rails_contexts
|
|
595
|
+
spec/fixtures/reports/rails_blockers_v0.1.json
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
CI is configured to run linting, specs, and gem packaging on Ruby 3.3 and 3.4.
|
|
599
|
+
The workflow configuration does not itself prove that remote CI has passed.
|
|
600
|
+
|
|
601
|
+
## 13. Runtime Architecture Beyond v0.1.0
|
|
602
|
+
|
|
603
|
+
The long-term architecture adds a runtime branch alongside static analysis:
|
|
604
|
+
|
|
605
|
+
```text
|
|
606
|
+
Static engine Runtime session
|
|
607
|
+
Rubydex + Prism Instrumentation + watchdog
|
|
608
|
+
| |
|
|
609
|
+
v v
|
|
610
|
+
Static findings Runtime events
|
|
611
|
+
| |
|
|
612
|
+
+---------------+------------------+
|
|
613
|
+
|
|
|
614
|
+
v
|
|
615
|
+
Fingerprint correlation
|
|
616
|
+
|
|
|
617
|
+
v
|
|
618
|
+
Confirmed / static-only / runtime-only findings
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
The foundational runtime contracts and recorder are implemented under
|
|
622
|
+
`lib/fiber_audit/runtime/`: immutable event/session values, strict redaction and
|
|
623
|
+
resource policy, injected clocks and sampling, bounded JSONL writing, explicit
|
|
624
|
+
drop accounting, and crash-tolerant session recording. The explicit `runtime`
|
|
625
|
+
command supervises a user-supplied command, injects a conditional Ruby boot,
|
|
626
|
+
forwards signals, preserves child status, and creates a distinct session for each
|
|
627
|
+
observed Ruby process.
|
|
628
|
+
|
|
629
|
+
Explicit runtime boot also installs a narrow observer for schedulers configured
|
|
630
|
+
through `Fiber.set_scheduler`. A scheduler-owned heartbeat fiber updates
|
|
631
|
+
monotonic progress; one process-local watchdog thread detects threshold crossings.
|
|
632
|
+
Each stall emits at most one start and one completion event plus a configured,
|
|
633
|
+
bounded set of project-relative frame events. An active-operation registry uses
|
|
634
|
+
only ephemeral thread/fiber identities and process-local sequences so targeted
|
|
635
|
+
operations can overlap scheduler stalls without retaining arguments or request
|
|
636
|
+
identifiers.
|
|
637
|
+
|
|
638
|
+
Watchdog state is explicit: disabled, absent, active, or unsupported. State and
|
|
639
|
+
stall events bypass random sampling but still consume all recorder rate, event,
|
|
640
|
+
record, and session limits. Runtime JSONL schema `1.0` and its `session_start`
|
|
641
|
+
contract remain unchanged; watchdog policy travels only in strict activation
|
|
642
|
+
settings, while state and policy measurements are ordinary bounded events.
|
|
643
|
+
Explicit boot installs narrow, idempotent `Module#prepend` wrappers for the
|
|
644
|
+
operations represented by FA1001–FA1007. One shared probe base owns monotonic
|
|
645
|
+
timing, sampled completion/abortion events, conservative direct project
|
|
646
|
+
callsites, recursion protection, and active-operation registration. Probe events
|
|
647
|
+
retain only canonical operations, duration, project-relative location,
|
|
648
|
+
ephemeral identities, operation sequence, and a small fixed set of Booleans.
|
|
649
|
+
Commands, URLs, addresses, ports, headers, payloads, return data, exception data,
|
|
650
|
+
and thread-local keys or values are never retained.
|
|
651
|
+
|
|
652
|
+
A process-local registry deactivates wrappers without attempting to remove Ruby
|
|
653
|
+
prepends. A narrow `Kernel#require` wrapper rescans only known standard-library
|
|
654
|
+
targets after successful loads; no unrestricted tracing, `load`, `const_missing`,
|
|
655
|
+
or autoload observation is used. Unknown ownership is skipped rather than mapped
|
|
656
|
+
to a project callsite.
|
|
657
|
+
|
|
658
|
+
Stage 5 adds `source: targeted_probe` and the event kinds `operation_started`,
|
|
659
|
+
`operation_completed`, and `operation_aborted`. These values use existing bounded
|
|
660
|
+
identifier fields and are backward-compatible additions to runtime JSONL schema
|
|
661
|
+
`1.0`; the envelope, payload keys, and original golden fixture remain unchanged.
|
|
662
|
+
|
|
663
|
+
Shutdown first deactivates probes, then makes scheduler callbacks inert, requests
|
|
664
|
+
heartbeat/watchdog stop, bounds and joins the watchdog thread, completes any open
|
|
665
|
+
stall, and only then closes the recorder. Fork rebinding discards inherited probe
|
|
666
|
+
and watchdog references before touching their locks and creates process-local
|
|
667
|
+
replacements.
|
|
668
|
+
|
|
669
|
+
Stage 6 adds Rails execution context detection. A bounded, PID-aware fiber-local
|
|
670
|
+
context stack tracks the current execution context during probe observations.
|
|
671
|
+
The fiber-local stack uses `Fiber.current` instance variables to avoid thread-local
|
|
672
|
+
key visibility, validates against `Context::ALL`, enforces `MAX_DEPTH = 32`, and
|
|
673
|
+
resets on fork. A process-local `RailsIntegration` class hooks into Rails boundaries
|
|
674
|
+
via `Module#prepend`: Rack middleware (`:middleware`), `ActionController::Metal#process_action`
|
|
675
|
+
(`:request`), `ActiveJob::Base#perform_now` (`:job`), and `ActionCable::Channel::Base#dispatch_action`
|
|
676
|
+
(`:websocket`). Wrappers consult the active integration before setting context and become
|
|
677
|
+
inert after deactivation or fork, preserving application semantics. The integration
|
|
678
|
+
supports late loading: hooks are installed when Rails components become available,
|
|
679
|
+
even after runtime boot. Probe observations snapshot the context once at start and
|
|
680
|
+
propagate it through active operations and events. Lifecycle wires context store and
|
|
681
|
+
Rails integration ownership, shutdown deactivates Rails integration before probes,
|
|
682
|
+
and fork rebinding resets context and rebuilds integration. JSONL schema 1.0 and
|
|
683
|
+
privacy requirements are preserved; no new schema fields are added.
|
|
684
|
+
|
|
685
|
+
Loading the gem normally performs no instrumentation, fibers, threads, or file
|
|
686
|
+
I/O. The observer is activated only by explicit runtime boot. A native operation
|
|
687
|
+
that retains Ruby's GVL can prevent the watchdog thread from running until the
|
|
688
|
+
operation returns; absence or unsupported monitoring and absence of stalls are
|
|
689
|
+
never clean certification.
|
|
690
|
+
|
|
691
|
+
Remaining runtime concepts include:
|
|
692
|
+
|
|
693
|
+
- static/runtime evidence merging;
|
|
694
|
+
- runtime coverage sufficient to support future `PASS` semantics.
|
|
695
|
+
|
|
696
|
+
Runtime components must continue to emit or enrich the common finding model
|
|
697
|
+
rather than establish a parallel reporting model.
|
|
698
|
+
|
|
699
|
+
## 14. Repository Map
|
|
700
|
+
|
|
701
|
+
```text
|
|
702
|
+
lib/fiber_audit.rb public loader
|
|
703
|
+
lib/fiber_audit/cli.rb command and exit-code boundary
|
|
704
|
+
lib/fiber_audit/project.rb root/config discovery
|
|
705
|
+
lib/fiber_audit/audit.rb static coordinator
|
|
706
|
+
lib/fiber_audit/configuration.rb validated static configuration
|
|
707
|
+
lib/fiber_audit/findings/ public result values
|
|
708
|
+
lib/fiber_audit/correlation/fingerprint.rb stable identity
|
|
709
|
+
lib/fiber_audit/suppressions/ suppression parsing and filtering
|
|
710
|
+
lib/fiber_audit/static/ semantic, call-site, context, rules
|
|
711
|
+
lib/fiber_audit/reporters/ text and JSON schema 1.0
|
|
712
|
+
lib/fiber_audit/runtime/ values, lifecycle, watchdog, probes
|
|
713
|
+
ARCHITECTURE.md supported architecture and boundaries
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
## 15. Maintaining This Document
|
|
717
|
+
|
|
718
|
+
Update this document when the supported architecture changes:
|
|
719
|
+
|
|
720
|
+
1. Describe components as implemented only when source and meaningful specs
|
|
721
|
+
exist.
|
|
722
|
+
2. Update diagrams when dependency direction changes.
|
|
723
|
+
3. Record external report-schema changes as versioned contract changes.
|
|
724
|
+
4. Keep future runtime architecture separate from current static behavior.
|
|
725
|
+
6. Never use the existence of a built gem artifact as evidence that a release or
|
|
726
|
+
architecture stage is complete.
|