fiber_audit 0.2.1 → 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 +97 -673
- data/CHANGELOG.md +52 -0
- data/README.md +163 -86
- data/lib/fiber_audit/cli.rb +19 -5
- data/lib/fiber_audit/configuration.rb +31 -6
- data/lib/fiber_audit/operation_semantics.rb +172 -0
- data/lib/fiber_audit/operation_vocabulary.rb +2 -1
- data/lib/fiber_audit/runtime/active_operations.rb +39 -9
- data/lib/fiber_audit/runtime/boot.rb +4 -0
- data/lib/fiber_audit/runtime/environment.rb +58 -0
- data/lib/fiber_audit/runtime/execution_context.rb +55 -43
- 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 +27 -4
- data/lib/fiber_audit/runtime/probes/subprocess.rb +89 -8
- data/lib/fiber_audit/runtime/probes/thread_state.rb +0 -14
- data/lib/fiber_audit/runtime/scheduler_evidence_classifier.rb +60 -0
- data/lib/fiber_audit/runtime/scheduler_observer.rb +54 -18
- data/lib/fiber_audit/runtime/scheduler_snapshot.rb +95 -0
- data/lib/fiber_audit/runtime/watchdog.rb +54 -4
- data/lib/fiber_audit/runtime.rb +5 -0
- data/lib/fiber_audit/static/call_site_extractor.rb +1 -0
- data/lib/fiber_audit/static/rules/base.rb +19 -0
- data/lib/fiber_audit/static/rules/blocking_subprocess.rb +67 -10
- data/lib/fiber_audit/static/rules/direct_socket.rb +54 -19
- data/lib/fiber_audit/static/rules/io_select.rb +6 -6
- data/lib/fiber_audit/static/rules/net_http_in_request.rb +11 -8
- data/lib/fiber_audit/static/rules/synchronization.rb +13 -8
- data/lib/fiber_audit/static/rules/thread_current_state.rb +20 -30
- data/lib/fiber_audit/static/rules/thread_join.rb +9 -7
- data/lib/fiber_audit/version.rb +1 -1
- metadata +6 -1
|
@@ -4,22 +4,72 @@ require_relative 'base'
|
|
|
4
4
|
require_relative '../../findings/evidence'
|
|
5
5
|
require_relative '../../correlation/fingerprint'
|
|
6
6
|
require_relative '../../findings/finding'
|
|
7
|
+
require_relative '../../operation_semantics'
|
|
7
8
|
require_relative '../../operation_vocabulary'
|
|
8
9
|
|
|
9
10
|
module FiberAudit
|
|
10
11
|
module Static
|
|
11
12
|
module Rules
|
|
13
|
+
# FA1001: Subprocess lifecycle operations that may interfere with
|
|
14
|
+
# the fiber scheduler. Detects subprocess creation, replacement,
|
|
15
|
+
# waiting, detachment, and stream lifecycle operations.
|
|
16
|
+
#
|
|
17
|
+
# Operations are classified into semantic categories:
|
|
18
|
+
# - creation: spawning a new process (info)
|
|
19
|
+
# - replacement: replacing the current process via exec (info)
|
|
20
|
+
# - waiting: blocking waits for subprocess completion (medium)
|
|
21
|
+
# - detach: detaching a subprocess without waiting (info)
|
|
22
|
+
# - stream: subprocess pipe/stream lifecycle via IO.popen (medium)
|
|
23
|
+
#
|
|
24
|
+
# Severity: info for creation/replacement/detach, medium for waits/stream.
|
|
25
|
+
# These are scheduler-cooperation requirements, so execution context does
|
|
26
|
+
# not by itself escalate them to critical.
|
|
12
27
|
class BlockingSubprocess < Base
|
|
13
28
|
id 'FA1001'
|
|
14
|
-
severity :
|
|
29
|
+
severity :medium
|
|
15
30
|
default_confidence :high
|
|
16
|
-
description '
|
|
31
|
+
description 'Subprocess lifecycle operations may interfere with the fiber scheduler'
|
|
17
32
|
|
|
18
33
|
TARGETS = OperationVocabulary::FA1001_TARGETS
|
|
19
34
|
BARE_KERNEL_METHODS = OperationVocabulary::FA1001_KERNEL_METHODS
|
|
20
35
|
|
|
21
|
-
|
|
22
|
-
|
|
36
|
+
OPERATION_CATEGORY = OperationSemantics::FA1001_CATEGORIES
|
|
37
|
+
|
|
38
|
+
# Per-category metadata
|
|
39
|
+
CATEGORY_METADATA = {
|
|
40
|
+
creation: {
|
|
41
|
+
severity: :info,
|
|
42
|
+
title: 'Subprocess creation',
|
|
43
|
+
message: 'Spawning a subprocess may leave background processes that outlive the fiber scheduler session.',
|
|
44
|
+
remediation: 'Track spawned processes or move subprocess creation outside the fiber-scheduled path.'
|
|
45
|
+
},
|
|
46
|
+
replacement: {
|
|
47
|
+
severity: :info,
|
|
48
|
+
title: 'Process replacement',
|
|
49
|
+
message: 'Process replacement via exec replaces the current process image, terminating the fiber scheduler.',
|
|
50
|
+
remediation: 'Avoid exec in fiber-scheduled code; prefer subprocess creation with explicit lifecycle management.'
|
|
51
|
+
},
|
|
52
|
+
waiting: {
|
|
53
|
+
severity: :medium,
|
|
54
|
+
title: 'Subprocess wait',
|
|
55
|
+
message: 'Subprocess waiting requires scheduler process-wait cooperation in a non-blocking Fiber.',
|
|
56
|
+
remediation: 'Verify scheduler process_wait support and runtime progress, ' \
|
|
57
|
+
'or move waits outside the fiber-scheduled path.'
|
|
58
|
+
},
|
|
59
|
+
detach: {
|
|
60
|
+
severity: :info,
|
|
61
|
+
title: 'Subprocess detach',
|
|
62
|
+
message: 'Detaching a subprocess may leave it unmanaged by the fiber scheduler.',
|
|
63
|
+
remediation: 'Avoid detaching subprocesses in fiber-scheduled code; use explicit lifecycle management.'
|
|
64
|
+
},
|
|
65
|
+
stream: {
|
|
66
|
+
severity: :medium,
|
|
67
|
+
title: 'Subprocess pipe stream',
|
|
68
|
+
message: 'Subprocess pipe I/O requires scheduler cooperation while the stream is open.',
|
|
69
|
+
remediation: 'Verify scheduler-aware I/O and runtime progress on the pipe, ' \
|
|
70
|
+
'or move pipe operations outside the fiber-scheduled path.'
|
|
71
|
+
}
|
|
72
|
+
}.freeze
|
|
23
73
|
|
|
24
74
|
def analyze(call_sites:)
|
|
25
75
|
call_sites.filter_map do |site|
|
|
@@ -60,11 +110,14 @@ module FiberAudit
|
|
|
60
110
|
def build_finding(site, match)
|
|
61
111
|
operation = "#{match[:constant]}.#{match[:method]}"
|
|
62
112
|
context = site.execution_context || :unknown
|
|
63
|
-
|
|
113
|
+
category = OperationSemantics.resolve(operation).category
|
|
114
|
+
metadata = CATEGORY_METADATA.fetch(category)
|
|
115
|
+
base_severity = metadata[:severity]
|
|
116
|
+
sev = advisory_severity(base_severity)
|
|
64
117
|
|
|
65
118
|
Finding.new(
|
|
66
119
|
rule_id: self.class.id,
|
|
67
|
-
title:
|
|
120
|
+
title: metadata[:title],
|
|
68
121
|
category: :subprocess,
|
|
69
122
|
severity: sev,
|
|
70
123
|
confidence: match[:confidence],
|
|
@@ -72,15 +125,19 @@ module FiberAudit
|
|
|
72
125
|
symbol: site.enclosing_symbol,
|
|
73
126
|
operation: operation,
|
|
74
127
|
execution_context: context,
|
|
75
|
-
message:
|
|
128
|
+
message: metadata[:message],
|
|
76
129
|
evidence: [
|
|
77
130
|
Evidence.new(
|
|
78
131
|
source: :static,
|
|
79
|
-
message: "Matched #{operation}",
|
|
80
|
-
details: {
|
|
132
|
+
message: "Matched #{operation} (#{category})",
|
|
133
|
+
details: {
|
|
134
|
+
receiver: match[:constant],
|
|
135
|
+
method: match[:method],
|
|
136
|
+
semantic: category
|
|
137
|
+
}
|
|
81
138
|
)
|
|
82
139
|
],
|
|
83
|
-
remediation:
|
|
140
|
+
remediation: metadata[:remediation]
|
|
84
141
|
)
|
|
85
142
|
end
|
|
86
143
|
end
|
|
@@ -4,28 +4,51 @@ require_relative 'base'
|
|
|
4
4
|
require_relative '../../findings/evidence'
|
|
5
5
|
require_relative '../../correlation/fingerprint'
|
|
6
6
|
require_relative '../../findings/finding'
|
|
7
|
+
require_relative '../../operation_semantics'
|
|
7
8
|
require_relative '../../operation_vocabulary'
|
|
8
9
|
|
|
9
10
|
module FiberAudit
|
|
10
11
|
module Static
|
|
11
12
|
module Rules
|
|
12
|
-
# FA1006
|
|
13
|
-
# scheduler-aware networking and block the scheduler thread.
|
|
13
|
+
# FA1006: Inventory and semantic classification for direct socket constructors.
|
|
14
14
|
class DirectSocket < Base
|
|
15
15
|
id 'FA1006'
|
|
16
|
-
severity :
|
|
16
|
+
severity :low
|
|
17
17
|
default_confidence :high
|
|
18
|
-
description '
|
|
18
|
+
description 'Direct socket constructors range from inventory-only allocation to endpoint setup.'
|
|
19
19
|
|
|
20
|
-
TITLE
|
|
20
|
+
TITLE = 'Direct socket construction'
|
|
21
21
|
CATEGORY = :network
|
|
22
|
-
|
|
23
22
|
EXACT = OperationVocabulary::FA1006_EXACT
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
CATEGORY_METADATA = {
|
|
24
|
+
socket_allocation: {
|
|
25
|
+
title: 'Direct socket allocation',
|
|
26
|
+
message: 'This constructor is inventory-level socket setup without a client connection; ' \
|
|
27
|
+
'later accept, connect, address resolution, or I/O may require scheduler cooperation.',
|
|
28
|
+
remediation: 'Treat allocation as inventory and verify scheduler-aware behavior at later ' \
|
|
29
|
+
'accept, connect, resolution, and I/O calls.'
|
|
30
|
+
},
|
|
31
|
+
socket_resolve_connect: {
|
|
32
|
+
title: 'Direct socket endpoint setup',
|
|
33
|
+
message: 'This constructor may resolve an address and establish a network endpoint, requiring ' \
|
|
34
|
+
'scheduler cooperation from address-resolution and I/O hooks.',
|
|
35
|
+
remediation: 'Verify address_resolve and scheduler-aware I/O hooks, and confirm runtime progress ' \
|
|
36
|
+
'for endpoint setup.'
|
|
37
|
+
},
|
|
38
|
+
socket_local_connect: {
|
|
39
|
+
title: 'Direct local-socket connection',
|
|
40
|
+
message: 'This constructor may establish a local socket connection and wait for endpoint progress.',
|
|
41
|
+
remediation: 'Verify runtime progress for local-socket connection and move blocking setup outside ' \
|
|
42
|
+
'the fiber-scheduled path.'
|
|
43
|
+
},
|
|
44
|
+
socket_constructor_unknown: {
|
|
45
|
+
title: 'Direct socket subclass construction',
|
|
46
|
+
message: 'This IPSocket subclass constructor has unknown allocation/connect semantics and may ' \
|
|
47
|
+
'require scheduler cooperation.',
|
|
48
|
+
remediation: 'Inspect the subclass constructor and verify scheduler-aware resolution, connection, ' \
|
|
49
|
+
'and I/O behavior.'
|
|
50
|
+
}
|
|
51
|
+
}.freeze
|
|
29
52
|
|
|
30
53
|
class << self
|
|
31
54
|
def title = TITLE
|
|
@@ -82,27 +105,39 @@ module FiberAudit
|
|
|
82
105
|
|
|
83
106
|
def build_finding(site, const)
|
|
84
107
|
operation = "#{const}.new"
|
|
85
|
-
|
|
108
|
+
profile = if EXACT.include?(const)
|
|
109
|
+
OperationSemantics.resolve(operation)
|
|
110
|
+
else
|
|
111
|
+
OperationSemantics.resolve_socket_constructor(operation)
|
|
112
|
+
end
|
|
113
|
+
metadata = CATEGORY_METADATA.fetch(profile.category)
|
|
86
114
|
|
|
87
115
|
Finding.new(
|
|
88
116
|
rule_id: self.class.id,
|
|
89
|
-
title:
|
|
117
|
+
title: metadata.fetch(:title),
|
|
90
118
|
category: self.class.category,
|
|
91
|
-
severity:
|
|
119
|
+
severity: advisory_severity(:low),
|
|
92
120
|
confidence: site.confidence,
|
|
93
121
|
location: site.location,
|
|
94
122
|
symbol: site.enclosing_symbol,
|
|
95
123
|
operation: operation,
|
|
96
|
-
execution_context:
|
|
97
|
-
message:
|
|
124
|
+
execution_context: site.execution_context || :unknown,
|
|
125
|
+
message: metadata.fetch(:message),
|
|
98
126
|
evidence: [
|
|
99
127
|
Evidence.new(
|
|
100
128
|
source: 'static_analysis',
|
|
101
|
-
message: "
|
|
102
|
-
details: {
|
|
129
|
+
message: "Matched #{operation} (#{profile.category})",
|
|
130
|
+
details: {
|
|
131
|
+
receiver_constant: const,
|
|
132
|
+
method: :new,
|
|
133
|
+
semantic: profile.category,
|
|
134
|
+
wait_possible: profile.wait_possible,
|
|
135
|
+
inventory_only: profile.inventory_only,
|
|
136
|
+
scheduler_capability: profile.scheduler_capability
|
|
137
|
+
}
|
|
103
138
|
)
|
|
104
139
|
],
|
|
105
|
-
remediation:
|
|
140
|
+
remediation: metadata.fetch(:remediation)
|
|
106
141
|
)
|
|
107
142
|
end
|
|
108
143
|
end
|
|
@@ -9,19 +9,19 @@ require_relative '../../operation_vocabulary'
|
|
|
9
9
|
module FiberAudit
|
|
10
10
|
module Static
|
|
11
11
|
module Rules
|
|
12
|
-
# FA1005: Detects explicit IO.select
|
|
13
|
-
#
|
|
12
|
+
# FA1005: Detects explicit IO.select scheduler-capability requirements.
|
|
13
|
+
# Advisory rule with :medium default.
|
|
14
14
|
class IOSelect < Base
|
|
15
15
|
id 'FA1005'
|
|
16
16
|
severity :medium
|
|
17
17
|
default_confidence :high
|
|
18
|
-
description 'Explicit IO.select
|
|
18
|
+
description 'Explicit IO.select requires scheduler io_select cooperation'
|
|
19
19
|
|
|
20
20
|
TITLE = 'Explicit IO.select call'
|
|
21
21
|
CATEGORY = :blocking_io
|
|
22
22
|
|
|
23
|
-
MESSAGE = 'IO.select
|
|
24
|
-
REMEDIATION = '
|
|
23
|
+
MESSAGE = 'IO.select requires scheduler io_select support when used from a non-blocking Fiber.'
|
|
24
|
+
REMEDIATION = 'Verify the selected scheduler implements io_select and confirm runtime progress under load.'
|
|
25
25
|
|
|
26
26
|
TARGETS = OperationVocabulary::FA1005_TARGETS
|
|
27
27
|
|
|
@@ -79,7 +79,7 @@ module FiberAudit
|
|
|
79
79
|
rule_id: self.class.id,
|
|
80
80
|
title: TITLE,
|
|
81
81
|
category: CATEGORY,
|
|
82
|
-
severity:
|
|
82
|
+
severity: advisory_severity(:medium),
|
|
83
83
|
confidence: confidence,
|
|
84
84
|
location: site.location,
|
|
85
85
|
symbol: site.enclosing_symbol,
|
|
@@ -9,19 +9,22 @@ require_relative '../../operation_vocabulary'
|
|
|
9
9
|
module FiberAudit
|
|
10
10
|
module Static
|
|
11
11
|
module Rules
|
|
12
|
-
# FA1007: Detects
|
|
12
|
+
# FA1007: Detects HTTP scheduler-cooperation requirements in request-like contexts.
|
|
13
13
|
# Targets Net::HTTP.{get,get_response,start,request}, URI.open, OpenURI.open_uri.
|
|
14
14
|
# Excludes Net::HTTP.get_print. Emits only for request/middleware/websocket/callback.
|
|
15
|
+
#
|
|
16
|
+
# Advisory rule with :medium default, no context ceiling.
|
|
15
17
|
class NetHTTPInRequest < Base
|
|
16
18
|
id 'FA1007'
|
|
17
|
-
severity :
|
|
19
|
+
severity :medium
|
|
18
20
|
default_confidence :high
|
|
19
|
-
description '
|
|
21
|
+
description 'HTTP calls require scheduler-aware DNS, socket, and TLS cooperation in request contexts'
|
|
20
22
|
|
|
21
|
-
TITLE = '
|
|
23
|
+
TITLE = 'HTTP scheduler-cooperation requirement'
|
|
22
24
|
CATEGORY = :network
|
|
23
|
-
MESSAGE = '
|
|
24
|
-
REMEDIATION = '
|
|
25
|
+
MESSAGE = 'HTTP activity requires scheduler-aware DNS and I/O cooperation in a non-blocking Fiber.'
|
|
26
|
+
REMEDIATION = 'Verify the selected HTTP/TLS/DNS stack and active scheduler ' \
|
|
27
|
+
'under load; investigate correlated stalls.'
|
|
25
28
|
|
|
26
29
|
NET_HTTP_METHODS = OperationVocabulary::FA1007_NET_HTTP_METHODS
|
|
27
30
|
URI_METHODS = OperationVocabulary::FA1007_URI_METHODS
|
|
@@ -82,7 +85,7 @@ module FiberAudit
|
|
|
82
85
|
def build_finding(site, match)
|
|
83
86
|
context = site.execution_context
|
|
84
87
|
sev, conf = if match[:type] == :net_http
|
|
85
|
-
[
|
|
88
|
+
[advisory_severity(:medium), site.confidence]
|
|
86
89
|
else
|
|
87
90
|
%i[medium low]
|
|
88
91
|
end
|
|
@@ -101,7 +104,7 @@ module FiberAudit
|
|
|
101
104
|
evidence: [
|
|
102
105
|
FiberAudit::Evidence.new(
|
|
103
106
|
source: 'static_analysis',
|
|
104
|
-
message: "
|
|
107
|
+
message: "HTTP scheduler-cooperation point: #{match[:operation]}",
|
|
105
108
|
details: { receiver: site.receiver_constant, method: site.method_name, context: context }
|
|
106
109
|
)
|
|
107
110
|
],
|
|
@@ -9,20 +9,24 @@ require_relative '../../operation_vocabulary'
|
|
|
9
9
|
module FiberAudit
|
|
10
10
|
module Static
|
|
11
11
|
module Rules
|
|
12
|
+
# FA1003: Thread synchronization primitives that may interfere with
|
|
13
|
+
# fiber scheduler cooperation. Advisory rule with :low default.
|
|
14
|
+
#
|
|
15
|
+
# Detects Mutex, ConditionVariable, Monitor, and MonitorMixin
|
|
16
|
+
# synchronization operations. try_lock is treated as :info since
|
|
17
|
+
# it's non-blocking but indicates thread-oriented synchronization.
|
|
12
18
|
class Synchronization < Base
|
|
13
19
|
id 'FA1003'
|
|
14
|
-
severity :
|
|
20
|
+
severity :low
|
|
15
21
|
default_confidence :high
|
|
16
|
-
description 'Thread synchronization
|
|
22
|
+
description 'Thread synchronization may interfere with fiber scheduler cooperation'
|
|
17
23
|
|
|
18
24
|
RULE_TITLE = 'Thread synchronization'
|
|
19
25
|
RULE_CATEGORY = :synchronization
|
|
20
26
|
TARGETS = OperationVocabulary::FA1003_TARGETS
|
|
21
|
-
TRY_LOCK_MSG = 'Mutex
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
REMEDIATION = 'Use scheduler-aware synchronization primitives, or verify ' \
|
|
25
|
-
'contention and scheduler behaviour under load.'
|
|
27
|
+
TRY_LOCK_MSG = 'Mutex#try_lock is non-blocking and identifies a thread-oriented coordination point.'
|
|
28
|
+
NORMAL_MSG = 'Synchronization contention requires scheduler block/unblock cooperation in a non-blocking Fiber.'
|
|
29
|
+
REMEDIATION = 'Verify scheduler coordination and contention behavior under load; investigate correlated stalls.'
|
|
26
30
|
|
|
27
31
|
def analyze(call_sites:)
|
|
28
32
|
explicit_monitor_mixins = explicit_monitor_mixin_classes(call_sites)
|
|
@@ -98,7 +102,8 @@ module FiberAudit
|
|
|
98
102
|
def build_finding(site, target, method)
|
|
99
103
|
try_lock = target == 'Mutex' && method == :try_lock
|
|
100
104
|
operation = "#{target}##{method}"
|
|
101
|
-
|
|
105
|
+
# try_lock has fixed :info, no config override
|
|
106
|
+
severity = try_lock ? :info : advisory_severity(:low)
|
|
102
107
|
confidence = try_lock ? :high : site.confidence
|
|
103
108
|
message = try_lock ? TRY_LOCK_MSG : NORMAL_MSG
|
|
104
109
|
evidence = Evidence.new(source: operation, message: message,
|
|
@@ -9,27 +9,28 @@ require_relative '../../operation_vocabulary'
|
|
|
9
9
|
module FiberAudit
|
|
10
10
|
module Static
|
|
11
11
|
module Rules
|
|
12
|
-
# Detect thread-variable
|
|
12
|
+
# FA1004: Detect thread-variable state access without inferring stored data.
|
|
13
13
|
class ThreadCurrentState < Base
|
|
14
14
|
id 'FA1004'
|
|
15
|
-
severity :
|
|
15
|
+
severity :medium
|
|
16
16
|
confidence :high
|
|
17
|
-
description 'Thread-
|
|
17
|
+
description 'Thread-variable access may share mutable state across fibers on one thread'
|
|
18
18
|
|
|
19
|
-
TITLE = 'Thread-
|
|
19
|
+
TITLE = 'Thread-variable access shared across fibers'
|
|
20
20
|
CATEGORY = :thread_local
|
|
21
|
-
MESSAGE = 'Thread
|
|
22
|
-
|
|
21
|
+
MESSAGE = 'Thread variables are visible to fibers sharing a thread. This access may expose mutable ' \
|
|
22
|
+
'state across concurrent work, but static analysis does not establish request-sensitive leakage.'
|
|
23
|
+
REMEDIATION = 'Prefer fiber-local storage (Fiber[:key]) or framework-provided ' \
|
|
24
|
+
'request-local state over thread_variable_get/set.'
|
|
23
25
|
|
|
24
26
|
THREAD_VARIABLE_METHODS = OperationVocabulary::FA1004_THREAD_VARIABLE_METHODS
|
|
25
|
-
INDEX_METHODS = OperationVocabulary::FA1004_INDEX_METHODS
|
|
26
27
|
|
|
27
28
|
def analyze(call_sites:)
|
|
28
29
|
findings = []
|
|
29
30
|
call_sites.each do |site|
|
|
30
31
|
next if skip?(site)
|
|
31
32
|
|
|
32
|
-
finding = match_thread_variable(site)
|
|
33
|
+
finding = match_thread_variable(site)
|
|
33
34
|
findings << finding if finding
|
|
34
35
|
end
|
|
35
36
|
findings
|
|
@@ -68,43 +69,32 @@ module FiberAudit
|
|
|
68
69
|
|
|
69
70
|
instance_match = site.receiver_constant == 'Thread' && site.receiver_source != 'Thread'
|
|
70
71
|
current_match = site.receiver_source == 'Thread.current'
|
|
71
|
-
|
|
72
72
|
return unless instance_match || current_match
|
|
73
73
|
|
|
74
|
-
build_finding(site,
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def match_index_op(site)
|
|
78
|
-
return unless INDEX_METHODS.include?(site.method_name)
|
|
79
|
-
return unless site.receiver_source == 'Thread.current'
|
|
80
|
-
|
|
81
|
-
build_finding(site, :medium, :high, operation(site))
|
|
74
|
+
build_finding(site, operation(site))
|
|
82
75
|
end
|
|
83
76
|
|
|
84
77
|
def operation(site)
|
|
85
|
-
|
|
86
|
-
"Thread.current.#{site.method_name}"
|
|
87
|
-
else
|
|
88
|
-
"Thread.#{site.method_name}"
|
|
89
|
-
end
|
|
78
|
+
"Thread.#{site.method_name}"
|
|
90
79
|
end
|
|
91
80
|
|
|
92
|
-
def build_finding(site,
|
|
93
|
-
context = site.execution_context
|
|
94
|
-
severity = severity_for(default_sev, context)
|
|
95
|
-
|
|
81
|
+
def build_finding(site, operation)
|
|
96
82
|
Finding.new(
|
|
97
83
|
rule_id: self.class.id,
|
|
98
84
|
title: TITLE,
|
|
99
85
|
category: CATEGORY,
|
|
100
|
-
severity:
|
|
101
|
-
confidence:
|
|
86
|
+
severity: advisory_severity(:medium),
|
|
87
|
+
confidence: :high,
|
|
102
88
|
location: site.location,
|
|
103
89
|
symbol: site.enclosing_symbol,
|
|
104
90
|
operation: operation,
|
|
105
|
-
execution_context:
|
|
91
|
+
execution_context: site.execution_context,
|
|
106
92
|
message: MESSAGE,
|
|
107
|
-
evidence: [Evidence.new(
|
|
93
|
+
evidence: [Evidence.new(
|
|
94
|
+
source: site.receiver_source,
|
|
95
|
+
message: "Matched thread-variable access via #{site.method_name}",
|
|
96
|
+
details: { operation: operation, receiver: site.receiver_source }
|
|
97
|
+
)],
|
|
108
98
|
remediation: REMEDIATION
|
|
109
99
|
)
|
|
110
100
|
end
|
|
@@ -9,7 +9,7 @@ require_relative '../../operation_vocabulary'
|
|
|
9
9
|
module FiberAudit
|
|
10
10
|
module Static
|
|
11
11
|
module Rules
|
|
12
|
-
# FA1002: Thread#join / Thread#value
|
|
12
|
+
# FA1002: Thread#join / Thread#value are scheduler coordination points.
|
|
13
13
|
#
|
|
14
14
|
# Matches join/value on Thread instances whose receiver_constant is
|
|
15
15
|
# 'Thread' but whose receiver_source is not the bare literal 'Thread'.
|
|
@@ -20,17 +20,19 @@ module FiberAudit
|
|
|
20
20
|
# and workspace-shadowed Thread (checked via workspace or
|
|
21
21
|
# workspace.semantic_index resolve_constant seam). Adapter errors from
|
|
22
22
|
# those seams are swallowed — they never raise.
|
|
23
|
+
#
|
|
24
|
+
# Advisory rule: uses advisory_severity (no context ceiling), default :low.
|
|
23
25
|
class ThreadJoin < Base
|
|
24
26
|
id 'FA1002'
|
|
25
|
-
severity :
|
|
27
|
+
severity :low
|
|
26
28
|
confidence :high
|
|
27
|
-
description '
|
|
29
|
+
description 'Thread waits require scheduler block/unblock cooperation'
|
|
28
30
|
|
|
29
31
|
TITLE = 'Thread wait'
|
|
30
32
|
CATEGORY = :synchronization
|
|
31
|
-
MESSAGE = '
|
|
32
|
-
REMEDIATION = '
|
|
33
|
-
'or move the
|
|
33
|
+
MESSAGE = 'Thread#join/value requires scheduler block/unblock cooperation in a non-blocking Fiber.'
|
|
34
|
+
REMEDIATION = 'Verify scheduler coordination and runtime progress, ' \
|
|
35
|
+
'or move the wait outside the fiber-scheduled path.'
|
|
34
36
|
TARGET_METHODS = OperationVocabulary::FA1002_METHODS
|
|
35
37
|
CANONICAL_OPS = OperationVocabulary::FA1002_OPERATIONS
|
|
36
38
|
DIRECT_CLASS_SOURCE = 'Thread'
|
|
@@ -73,7 +75,7 @@ module FiberAudit
|
|
|
73
75
|
def build_finding(site)
|
|
74
76
|
loc = site.location
|
|
75
77
|
op = "Thread.#{site.method_name}"
|
|
76
|
-
sev =
|
|
78
|
+
sev = advisory_severity(self.class.severity)
|
|
77
79
|
conf = site.receiver_source == 'Thread.current' ? :high : site.confidence
|
|
78
80
|
|
|
79
81
|
Finding.new(
|
data/lib/fiber_audit/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fiber_audit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- FiberAudit Contributors
|
|
@@ -61,6 +61,7 @@ files:
|
|
|
61
61
|
- lib/fiber_audit/findings/finding.rb
|
|
62
62
|
- lib/fiber_audit/findings/location.rb
|
|
63
63
|
- lib/fiber_audit/findings/severity.rb
|
|
64
|
+
- lib/fiber_audit/operation_semantics.rb
|
|
64
65
|
- lib/fiber_audit/operation_vocabulary.rb
|
|
65
66
|
- lib/fiber_audit/project.rb
|
|
66
67
|
- lib/fiber_audit/reporters/base.rb
|
|
@@ -80,6 +81,8 @@ files:
|
|
|
80
81
|
- lib/fiber_audit/runtime/lifecycle.rb
|
|
81
82
|
- lib/fiber_audit/runtime/limits.rb
|
|
82
83
|
- lib/fiber_audit/runtime/location.rb
|
|
84
|
+
- lib/fiber_audit/runtime/operation_liveness_monitor.rb
|
|
85
|
+
- lib/fiber_audit/runtime/operation_liveness_policy.rb
|
|
83
86
|
- lib/fiber_audit/runtime/policy.rb
|
|
84
87
|
- lib/fiber_audit/runtime/probes/base.rb
|
|
85
88
|
- lib/fiber_audit/runtime/probes/http.rb
|
|
@@ -94,7 +97,9 @@ files:
|
|
|
94
97
|
- lib/fiber_audit/runtime/recorder.rb
|
|
95
98
|
- lib/fiber_audit/runtime/redactor.rb
|
|
96
99
|
- lib/fiber_audit/runtime/sampler.rb
|
|
100
|
+
- lib/fiber_audit/runtime/scheduler_evidence_classifier.rb
|
|
97
101
|
- lib/fiber_audit/runtime/scheduler_observer.rb
|
|
102
|
+
- lib/fiber_audit/runtime/scheduler_snapshot.rb
|
|
98
103
|
- lib/fiber_audit/runtime/session.rb
|
|
99
104
|
- lib/fiber_audit/runtime/supervisor.rb
|
|
100
105
|
- lib/fiber_audit/runtime/validation.rb
|